codex-orchestrator 0.1.51 → 2.0.1

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 (570) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +45 -389
  3. package/dist/src/index.d.ts +16 -46
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +8 -24
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/v2/acceptance-proof.d.ts +111 -0
  8. package/dist/src/v2/acceptance-proof.d.ts.map +1 -0
  9. package/dist/src/v2/acceptance-proof.js +427 -0
  10. package/dist/src/v2/acceptance-proof.js.map +1 -0
  11. package/dist/src/v2/adapters/command.d.ts.map +1 -0
  12. package/dist/src/v2/adapters/command.js.map +1 -0
  13. package/dist/src/v2/adapters/durable-atomic-file.d.ts.map +1 -0
  14. package/dist/src/v2/adapters/durable-atomic-file.js.map +1 -0
  15. package/dist/src/v2/adapters/gh-cli.d.ts.map +1 -0
  16. package/dist/src/v2/adapters/gh-cli.js.map +1 -0
  17. package/dist/src/v2/adapters/gh-issue-adapter.d.ts.map +1 -0
  18. package/dist/src/v2/adapters/gh-issue-adapter.js.map +1 -0
  19. package/dist/src/v2/adapters/gh-pull-request-adapter.d.ts.map +1 -0
  20. package/dist/src/v2/adapters/gh-pull-request-adapter.js.map +1 -0
  21. package/dist/src/v2/adapters/issues.d.ts.map +1 -0
  22. package/dist/src/v2/adapters/issues.js.map +1 -0
  23. package/dist/src/v2/adapters/mission-coordinator-lock.d.ts.map +1 -0
  24. package/dist/src/v2/adapters/mission-coordinator-lock.js.map +1 -0
  25. package/dist/src/v2/adapters/path-policy.d.ts.map +1 -0
  26. package/dist/src/v2/adapters/path-policy.js.map +1 -0
  27. package/dist/src/v2/adapters/pull-requests.d.ts.map +1 -0
  28. package/dist/src/v2/adapters/pull-requests.js.map +1 -0
  29. package/dist/src/v2/adapters/target-activity-fence.d.ts.map +1 -0
  30. package/dist/src/{runner → v2/adapters}/target-activity-fence.js +1 -1
  31. package/dist/src/v2/adapters/target-activity-fence.js.map +1 -0
  32. package/dist/src/{git → v2/adapters}/worktree.d.ts +1 -1
  33. package/dist/src/v2/adapters/worktree.d.ts.map +1 -0
  34. package/dist/src/{git → v2/adapters}/worktree.js +2 -2
  35. package/dist/src/v2/adapters/worktree.js.map +1 -0
  36. package/dist/src/v2/atomic-store.d.ts +36 -0
  37. package/dist/src/v2/atomic-store.d.ts.map +1 -0
  38. package/dist/src/v2/atomic-store.js +328 -0
  39. package/dist/src/v2/atomic-store.js.map +1 -0
  40. package/dist/src/v2/candidate-cli.d.ts +22 -0
  41. package/dist/src/v2/candidate-cli.d.ts.map +1 -0
  42. package/dist/src/v2/candidate-cli.js +161 -0
  43. package/dist/src/v2/candidate-cli.js.map +1 -0
  44. package/dist/src/v2/checked-change.d.ts +49 -0
  45. package/dist/src/v2/checked-change.d.ts.map +1 -0
  46. package/dist/src/v2/checked-change.js +139 -0
  47. package/dist/src/v2/checked-change.js.map +1 -0
  48. package/dist/src/v2/cli-contract.d.ts +8 -0
  49. package/dist/src/v2/cli-contract.d.ts.map +1 -0
  50. package/dist/src/v2/cli-contract.js +32 -0
  51. package/dist/src/v2/cli-contract.js.map +1 -0
  52. package/dist/src/v2/codex-process.d.ts +71 -0
  53. package/dist/src/v2/codex-process.d.ts.map +1 -0
  54. package/dist/src/v2/codex-process.js +404 -0
  55. package/dist/src/v2/codex-process.js.map +1 -0
  56. package/dist/src/v2/config.d.ts +45 -0
  57. package/dist/src/v2/config.d.ts.map +1 -0
  58. package/dist/src/v2/config.js +139 -0
  59. package/dist/src/v2/config.js.map +1 -0
  60. package/dist/src/v2/containment.d.ts +55 -0
  61. package/dist/src/v2/containment.d.ts.map +1 -0
  62. package/dist/src/v2/containment.js +370 -0
  63. package/dist/src/v2/containment.js.map +1 -0
  64. package/dist/src/v2/implementation-report.d.ts +18 -0
  65. package/dist/src/v2/implementation-report.d.ts.map +1 -0
  66. package/dist/src/v2/implementation-report.js +147 -0
  67. package/dist/src/v2/implementation-report.js.map +1 -0
  68. package/dist/src/v2/legacy-cutover.d.ts +52 -0
  69. package/dist/src/v2/legacy-cutover.d.ts.map +1 -0
  70. package/dist/src/v2/legacy-cutover.js +87 -0
  71. package/dist/src/v2/legacy-cutover.js.map +1 -0
  72. package/dist/src/v2/mobile-lease.d.ts +97 -0
  73. package/dist/src/v2/mobile-lease.d.ts.map +1 -0
  74. package/dist/src/v2/mobile-lease.js +253 -0
  75. package/dist/src/v2/mobile-lease.js.map +1 -0
  76. package/dist/src/v2/proof-report.d.ts +118 -0
  77. package/dist/src/v2/proof-report.d.ts.map +1 -0
  78. package/dist/src/v2/proof-report.js +703 -0
  79. package/dist/src/v2/proof-report.js.map +1 -0
  80. package/dist/src/v2/proof-store.d.ts +42 -0
  81. package/dist/src/v2/proof-store.d.ts.map +1 -0
  82. package/dist/src/v2/proof-store.js +180 -0
  83. package/dist/src/v2/proof-store.js.map +1 -0
  84. package/dist/src/v2/report-envelope.d.ts +4 -0
  85. package/dist/src/v2/report-envelope.d.ts.map +1 -0
  86. package/dist/src/v2/report-envelope.js +46 -0
  87. package/dist/src/v2/report-envelope.js.map +1 -0
  88. package/dist/src/v2/run-issue.d.ts +213 -0
  89. package/dist/src/v2/run-issue.d.ts.map +1 -0
  90. package/dist/src/v2/run-issue.js +906 -0
  91. package/dist/src/v2/run-issue.js.map +1 -0
  92. package/dist/src/v2/run-store.d.ts +136 -0
  93. package/dist/src/v2/run-store.d.ts.map +1 -0
  94. package/dist/src/v2/run-store.js +346 -0
  95. package/dist/src/v2/run-store.js.map +1 -0
  96. package/dist/src/v2/runtime-assets.d.ts +31 -0
  97. package/dist/src/v2/runtime-assets.d.ts.map +1 -0
  98. package/dist/src/v2/runtime-assets.js +473 -0
  99. package/dist/src/v2/runtime-assets.js.map +1 -0
  100. package/dist/src/v2/runtime.d.ts +137 -0
  101. package/dist/src/v2/runtime.d.ts.map +1 -0
  102. package/dist/src/v2/runtime.js +847 -0
  103. package/dist/src/v2/runtime.js.map +1 -0
  104. package/dist/src/v2/setup-cli.d.ts +5 -0
  105. package/dist/src/v2/setup-cli.d.ts.map +1 -0
  106. package/dist/src/v2/setup-cli.js +83 -0
  107. package/dist/src/v2/setup-cli.js.map +1 -0
  108. package/dist/src/v2/setup-runtime.d.ts +6 -0
  109. package/dist/src/v2/setup-runtime.d.ts.map +1 -0
  110. package/dist/src/v2/setup-runtime.js +213 -0
  111. package/dist/src/v2/setup-runtime.js.map +1 -0
  112. package/dist/src/v2/setup-store.d.ts +19 -0
  113. package/dist/src/v2/setup-store.d.ts.map +1 -0
  114. package/dist/src/v2/setup-store.js +208 -0
  115. package/dist/src/v2/setup-store.js.map +1 -0
  116. package/dist/src/v2/setup.d.ts +147 -0
  117. package/dist/src/v2/setup.d.ts.map +1 -0
  118. package/dist/src/v2/setup.js +529 -0
  119. package/dist/src/v2/setup.js.map +1 -0
  120. package/docs/deep-dive.md +54 -803
  121. package/internal-skills/acceptance-proof/SKILL.md +14 -0
  122. package/internal-skills/acceptance-proof/references/android.md +14 -0
  123. package/internal-skills/acceptance-proof/references/browser.md +13 -0
  124. package/internal-skills/acceptance-proof/references/ios.md +14 -0
  125. package/internal-skills/acceptance-proof/tools/android-lease.mjs +280 -0
  126. package/internal-skills/acceptance-proof/tools/ios-lease.mjs +288 -0
  127. package/internal-skills/agent-auto/SKILL.md +14 -0
  128. package/package.json +11 -18
  129. package/bridge-runtime.json +0 -2719
  130. package/dist/src/bridge-runtime.d.ts +0 -17
  131. package/dist/src/bridge-runtime.d.ts.map +0 -1
  132. package/dist/src/bridge-runtime.js +0 -170
  133. package/dist/src/bridge-runtime.js.map +0 -1
  134. package/dist/src/cli.d.ts +0 -3
  135. package/dist/src/cli.d.ts.map +0 -1
  136. package/dist/src/cli.js +0 -569
  137. package/dist/src/cli.js.map +0 -1
  138. package/dist/src/codex/command-adapter.d.ts +0 -47
  139. package/dist/src/codex/command-adapter.d.ts.map +0 -1
  140. package/dist/src/codex/command-adapter.js +0 -169
  141. package/dist/src/codex/command-adapter.js.map +0 -1
  142. package/dist/src/codex/mobile-device-guard.d.ts +0 -7
  143. package/dist/src/codex/mobile-device-guard.d.ts.map +0 -1
  144. package/dist/src/codex/mobile-device-guard.js +0 -63
  145. package/dist/src/codex/mobile-device-guard.js.map +0 -1
  146. package/dist/src/config/constants.d.ts +0 -6
  147. package/dist/src/config/constants.d.ts.map +0 -1
  148. package/dist/src/config/constants.js +0 -19
  149. package/dist/src/config/constants.js.map +0 -1
  150. package/dist/src/config/schema.d.ts +0 -234
  151. package/dist/src/config/schema.d.ts.map +0 -1
  152. package/dist/src/config/schema.js +0 -673
  153. package/dist/src/config/schema.js.map +0 -1
  154. package/dist/src/fs/durable-atomic-file.d.ts.map +0 -1
  155. package/dist/src/fs/durable-atomic-file.js.map +0 -1
  156. package/dist/src/git/base-branch.d.ts +0 -26
  157. package/dist/src/git/base-branch.d.ts.map +0 -1
  158. package/dist/src/git/base-branch.js +0 -45
  159. package/dist/src/git/base-branch.js.map +0 -1
  160. package/dist/src/git/worktree.d.ts.map +0 -1
  161. package/dist/src/git/worktree.js.map +0 -1
  162. package/dist/src/github/gh-cli.d.ts.map +0 -1
  163. package/dist/src/github/gh-cli.js.map +0 -1
  164. package/dist/src/github/gh-issue-adapter.d.ts.map +0 -1
  165. package/dist/src/github/gh-issue-adapter.js.map +0 -1
  166. package/dist/src/github/gh-pull-request-adapter.d.ts.map +0 -1
  167. package/dist/src/github/gh-pull-request-adapter.js.map +0 -1
  168. package/dist/src/github/issues.d.ts.map +0 -1
  169. package/dist/src/github/issues.js.map +0 -1
  170. package/dist/src/github/pull-requests.d.ts.map +0 -1
  171. package/dist/src/github/pull-requests.js.map +0 -1
  172. package/dist/src/package-info.d.ts +0 -6
  173. package/dist/src/package-info.d.ts.map +0 -1
  174. package/dist/src/package-info.js +0 -13
  175. package/dist/src/package-info.js.map +0 -1
  176. package/dist/src/path-policy.d.ts.map +0 -1
  177. package/dist/src/path-policy.js.map +0 -1
  178. package/dist/src/process/command.d.ts.map +0 -1
  179. package/dist/src/process/command.js.map +0 -1
  180. package/dist/src/review-handoff.d.ts +0 -3
  181. package/dist/src/review-handoff.d.ts.map +0 -1
  182. package/dist/src/review-handoff.js +0 -8
  183. package/dist/src/review-handoff.js.map +0 -1
  184. package/dist/src/runner/acceptance-proof-loop.d.ts +0 -97
  185. package/dist/src/runner/acceptance-proof-loop.d.ts.map +0 -1
  186. package/dist/src/runner/acceptance-proof-loop.js +0 -404
  187. package/dist/src/runner/acceptance-proof-loop.js.map +0 -1
  188. package/dist/src/runner/acceptance-proof-runner.d.ts +0 -24
  189. package/dist/src/runner/acceptance-proof-runner.d.ts.map +0 -1
  190. package/dist/src/runner/acceptance-proof-runner.js +0 -121
  191. package/dist/src/runner/acceptance-proof-runner.js.map +0 -1
  192. package/dist/src/runner/acceptance-proof.d.ts +0 -189
  193. package/dist/src/runner/acceptance-proof.d.ts.map +0 -1
  194. package/dist/src/runner/acceptance-proof.js +0 -594
  195. package/dist/src/runner/acceptance-proof.js.map +0 -1
  196. package/dist/src/runner/agent-attempt.d.ts +0 -105
  197. package/dist/src/runner/agent-attempt.d.ts.map +0 -1
  198. package/dist/src/runner/agent-attempt.js +0 -245
  199. package/dist/src/runner/agent-attempt.js.map +0 -1
  200. package/dist/src/runner/android-visual-proof-command.d.ts +0 -26
  201. package/dist/src/runner/android-visual-proof-command.d.ts.map +0 -1
  202. package/dist/src/runner/android-visual-proof-command.js +0 -592
  203. package/dist/src/runner/android-visual-proof-command.js.map +0 -1
  204. package/dist/src/runner/auto-visual-proof-command.d.ts +0 -15
  205. package/dist/src/runner/auto-visual-proof-command.d.ts.map +0 -1
  206. package/dist/src/runner/auto-visual-proof-command.js +0 -64
  207. package/dist/src/runner/auto-visual-proof-command.js.map +0 -1
  208. package/dist/src/runner/browser-proof-contract.d.ts +0 -134
  209. package/dist/src/runner/browser-proof-contract.d.ts.map +0 -1
  210. package/dist/src/runner/browser-proof-contract.js +0 -400
  211. package/dist/src/runner/browser-proof-contract.js.map +0 -1
  212. package/dist/src/runner/browser-visual-proof-command.d.ts +0 -101
  213. package/dist/src/runner/browser-visual-proof-command.d.ts.map +0 -1
  214. package/dist/src/runner/browser-visual-proof-command.js +0 -532
  215. package/dist/src/runner/browser-visual-proof-command.js.map +0 -1
  216. package/dist/src/runner/command-utils.d.ts +0 -15
  217. package/dist/src/runner/command-utils.d.ts.map +0 -1
  218. package/dist/src/runner/command-utils.js +0 -228
  219. package/dist/src/runner/command-utils.js.map +0 -1
  220. package/dist/src/runner/completion-report.d.ts +0 -121
  221. package/dist/src/runner/completion-report.d.ts.map +0 -1
  222. package/dist/src/runner/completion-report.js +0 -381
  223. package/dist/src/runner/completion-report.js.map +0 -1
  224. package/dist/src/runner/context-snapshot.d.ts +0 -33
  225. package/dist/src/runner/context-snapshot.d.ts.map +0 -1
  226. package/dist/src/runner/context-snapshot.js +0 -99
  227. package/dist/src/runner/context-snapshot.js.map +0 -1
  228. package/dist/src/runner/daemon-command.d.ts +0 -30
  229. package/dist/src/runner/daemon-command.d.ts.map +0 -1
  230. package/dist/src/runner/daemon-command.js +0 -262
  231. package/dist/src/runner/daemon-command.js.map +0 -1
  232. package/dist/src/runner/doctor-command.d.ts +0 -41
  233. package/dist/src/runner/doctor-command.d.ts.map +0 -1
  234. package/dist/src/runner/doctor-command.js +0 -177
  235. package/dist/src/runner/doctor-command.js.map +0 -1
  236. package/dist/src/runner/durable-run-summary.d.ts +0 -80
  237. package/dist/src/runner/durable-run-summary.d.ts.map +0 -1
  238. package/dist/src/runner/durable-run-summary.js +0 -200
  239. package/dist/src/runner/durable-run-summary.js.map +0 -1
  240. package/dist/src/runner/flutter-sdk-discovery.d.ts +0 -5
  241. package/dist/src/runner/flutter-sdk-discovery.d.ts.map +0 -1
  242. package/dist/src/runner/flutter-sdk-discovery.js +0 -103
  243. package/dist/src/runner/flutter-sdk-discovery.js.map +0 -1
  244. package/dist/src/runner/fresh-context-review.d.ts +0 -22
  245. package/dist/src/runner/fresh-context-review.d.ts.map +0 -1
  246. package/dist/src/runner/fresh-context-review.js +0 -177
  247. package/dist/src/runner/fresh-context-review.js.map +0 -1
  248. package/dist/src/runner/handoff-evidence.d.ts +0 -141
  249. package/dist/src/runner/handoff-evidence.d.ts.map +0 -1
  250. package/dist/src/runner/handoff-evidence.js +0 -543
  251. package/dist/src/runner/handoff-evidence.js.map +0 -1
  252. package/dist/src/runner/hygiene-cleanup.d.ts +0 -15
  253. package/dist/src/runner/hygiene-cleanup.d.ts.map +0 -1
  254. package/dist/src/runner/hygiene-cleanup.js +0 -25
  255. package/dist/src/runner/hygiene-cleanup.js.map +0 -1
  256. package/dist/src/runner/ios-visual-proof-command.d.ts +0 -25
  257. package/dist/src/runner/ios-visual-proof-command.d.ts.map +0 -1
  258. package/dist/src/runner/ios-visual-proof-command.js +0 -353
  259. package/dist/src/runner/ios-visual-proof-command.js.map +0 -1
  260. package/dist/src/runner/issue-state-machine.d.ts +0 -39
  261. package/dist/src/runner/issue-state-machine.d.ts.map +0 -1
  262. package/dist/src/runner/issue-state-machine.js +0 -124
  263. package/dist/src/runner/issue-state-machine.js.map +0 -1
  264. package/dist/src/runner/issue-tree.d.ts +0 -65
  265. package/dist/src/runner/issue-tree.d.ts.map +0 -1
  266. package/dist/src/runner/issue-tree.js +0 -474
  267. package/dist/src/runner/issue-tree.js.map +0 -1
  268. package/dist/src/runner/lifecycle-events.d.ts +0 -44
  269. package/dist/src/runner/lifecycle-events.d.ts.map +0 -1
  270. package/dist/src/runner/lifecycle-events.js +0 -108
  271. package/dist/src/runner/lifecycle-events.js.map +0 -1
  272. package/dist/src/runner/local-execution-session.d.ts +0 -112
  273. package/dist/src/runner/local-execution-session.d.ts.map +0 -1
  274. package/dist/src/runner/local-execution-session.js +0 -1064
  275. package/dist/src/runner/local-execution-session.js.map +0 -1
  276. package/dist/src/runner/local-state.d.ts +0 -44
  277. package/dist/src/runner/local-state.d.ts.map +0 -1
  278. package/dist/src/runner/local-state.js +0 -150
  279. package/dist/src/runner/local-state.js.map +0 -1
  280. package/dist/src/runner/mission-activation.d.ts +0 -29
  281. package/dist/src/runner/mission-activation.d.ts.map +0 -1
  282. package/dist/src/runner/mission-activation.js +0 -54
  283. package/dist/src/runner/mission-activation.js.map +0 -1
  284. package/dist/src/runner/mission-agent-broker.d.ts +0 -53
  285. package/dist/src/runner/mission-agent-broker.d.ts.map +0 -1
  286. package/dist/src/runner/mission-agent-broker.js +0 -182
  287. package/dist/src/runner/mission-agent-broker.js.map +0 -1
  288. package/dist/src/runner/mission-cancellation.d.ts +0 -77
  289. package/dist/src/runner/mission-cancellation.d.ts.map +0 -1
  290. package/dist/src/runner/mission-cancellation.js +0 -180
  291. package/dist/src/runner/mission-cancellation.js.map +0 -1
  292. package/dist/src/runner/mission-canonical-path.d.ts +0 -34
  293. package/dist/src/runner/mission-canonical-path.d.ts.map +0 -1
  294. package/dist/src/runner/mission-canonical-path.js +0 -121
  295. package/dist/src/runner/mission-canonical-path.js.map +0 -1
  296. package/dist/src/runner/mission-capability-kernel.d.ts +0 -34
  297. package/dist/src/runner/mission-capability-kernel.d.ts.map +0 -1
  298. package/dist/src/runner/mission-capability-kernel.js +0 -158
  299. package/dist/src/runner/mission-capability-kernel.js.map +0 -1
  300. package/dist/src/runner/mission-check-policy.d.ts +0 -17
  301. package/dist/src/runner/mission-check-policy.d.ts.map +0 -1
  302. package/dist/src/runner/mission-check-policy.js +0 -31
  303. package/dist/src/runner/mission-check-policy.js.map +0 -1
  304. package/dist/src/runner/mission-coordinator-lock.d.ts.map +0 -1
  305. package/dist/src/runner/mission-coordinator-lock.js.map +0 -1
  306. package/dist/src/runner/mission-deployment.d.ts +0 -21
  307. package/dist/src/runner/mission-deployment.d.ts.map +0 -1
  308. package/dist/src/runner/mission-deployment.js +0 -108
  309. package/dist/src/runner/mission-deployment.js.map +0 -1
  310. package/dist/src/runner/mission-evaluation.d.ts +0 -61
  311. package/dist/src/runner/mission-evaluation.d.ts.map +0 -1
  312. package/dist/src/runner/mission-evaluation.js +0 -183
  313. package/dist/src/runner/mission-evaluation.js.map +0 -1
  314. package/dist/src/runner/mission-executor-probe.d.ts +0 -10
  315. package/dist/src/runner/mission-executor-probe.d.ts.map +0 -1
  316. package/dist/src/runner/mission-executor-probe.js +0 -180
  317. package/dist/src/runner/mission-executor-probe.js.map +0 -1
  318. package/dist/src/runner/mission-git-contracts.d.ts +0 -75
  319. package/dist/src/runner/mission-git-contracts.d.ts.map +0 -1
  320. package/dist/src/runner/mission-git-contracts.js +0 -201
  321. package/dist/src/runner/mission-git-contracts.js.map +0 -1
  322. package/dist/src/runner/mission-git-runtime-owner-ref.d.ts +0 -13
  323. package/dist/src/runner/mission-git-runtime-owner-ref.d.ts.map +0 -1
  324. package/dist/src/runner/mission-git-runtime-owner-ref.js +0 -111
  325. package/dist/src/runner/mission-git-runtime-owner-ref.js.map +0 -1
  326. package/dist/src/runner/mission-git-transaction.d.ts +0 -97
  327. package/dist/src/runner/mission-git-transaction.d.ts.map +0 -1
  328. package/dist/src/runner/mission-git-transaction.js +0 -1009
  329. package/dist/src/runner/mission-git-transaction.js.map +0 -1
  330. package/dist/src/runner/mission-identifiers.d.ts +0 -31
  331. package/dist/src/runner/mission-identifiers.d.ts.map +0 -1
  332. package/dist/src/runner/mission-identifiers.js +0 -75
  333. package/dist/src/runner/mission-identifiers.js.map +0 -1
  334. package/dist/src/runner/mission-input-snapshot.d.ts +0 -21
  335. package/dist/src/runner/mission-input-snapshot.d.ts.map +0 -1
  336. package/dist/src/runner/mission-input-snapshot.js +0 -63
  337. package/dist/src/runner/mission-input-snapshot.js.map +0 -1
  338. package/dist/src/runner/mission-ownership.d.ts +0 -38
  339. package/dist/src/runner/mission-ownership.d.ts.map +0 -1
  340. package/dist/src/runner/mission-ownership.js +0 -88
  341. package/dist/src/runner/mission-ownership.js.map +0 -1
  342. package/dist/src/runner/mission-patch-audit.d.ts +0 -21
  343. package/dist/src/runner/mission-patch-audit.d.ts.map +0 -1
  344. package/dist/src/runner/mission-patch-audit.js +0 -186
  345. package/dist/src/runner/mission-patch-audit.js.map +0 -1
  346. package/dist/src/runner/mission-path-language.d.ts +0 -6
  347. package/dist/src/runner/mission-path-language.d.ts.map +0 -1
  348. package/dist/src/runner/mission-path-language.js +0 -106
  349. package/dist/src/runner/mission-path-language.js.map +0 -1
  350. package/dist/src/runner/mission-permit-authority.d.ts +0 -26
  351. package/dist/src/runner/mission-permit-authority.d.ts.map +0 -1
  352. package/dist/src/runner/mission-permit-authority.js +0 -145
  353. package/dist/src/runner/mission-permit-authority.js.map +0 -1
  354. package/dist/src/runner/mission-plan-parent-coordinator.d.ts +0 -87
  355. package/dist/src/runner/mission-plan-parent-coordinator.d.ts.map +0 -1
  356. package/dist/src/runner/mission-plan-parent-coordinator.js +0 -441
  357. package/dist/src/runner/mission-plan-parent-coordinator.js.map +0 -1
  358. package/dist/src/runner/mission-plan-parent.d.ts +0 -156
  359. package/dist/src/runner/mission-plan-parent.d.ts.map +0 -1
  360. package/dist/src/runner/mission-plan-parent.js +0 -589
  361. package/dist/src/runner/mission-plan-parent.js.map +0 -1
  362. package/dist/src/runner/mission-process-executor.d.ts +0 -25
  363. package/dist/src/runner/mission-process-executor.d.ts.map +0 -1
  364. package/dist/src/runner/mission-process-executor.js +0 -182
  365. package/dist/src/runner/mission-process-executor.js.map +0 -1
  366. package/dist/src/runner/mission-publication-git.d.ts +0 -21
  367. package/dist/src/runner/mission-publication-git.d.ts.map +0 -1
  368. package/dist/src/runner/mission-publication-git.js +0 -61
  369. package/dist/src/runner/mission-publication-git.js.map +0 -1
  370. package/dist/src/runner/mission-publication.d.ts +0 -192
  371. package/dist/src/runner/mission-publication.d.ts.map +0 -1
  372. package/dist/src/runner/mission-publication.js +0 -1001
  373. package/dist/src/runner/mission-publication.js.map +0 -1
  374. package/dist/src/runner/mission-quarantine-patch-executor.d.ts +0 -29
  375. package/dist/src/runner/mission-quarantine-patch-executor.d.ts.map +0 -1
  376. package/dist/src/runner/mission-quarantine-patch-executor.js +0 -121
  377. package/dist/src/runner/mission-quarantine-patch-executor.js.map +0 -1
  378. package/dist/src/runner/mission-repository-executor.d.ts +0 -54
  379. package/dist/src/runner/mission-repository-executor.d.ts.map +0 -1
  380. package/dist/src/runner/mission-repository-executor.js +0 -192
  381. package/dist/src/runner/mission-repository-executor.js.map +0 -1
  382. package/dist/src/runner/mission-repository-observer.d.ts +0 -8
  383. package/dist/src/runner/mission-repository-observer.d.ts.map +0 -1
  384. package/dist/src/runner/mission-repository-observer.js +0 -24
  385. package/dist/src/runner/mission-repository-observer.js.map +0 -1
  386. package/dist/src/runner/mission-reservations.d.ts +0 -15
  387. package/dist/src/runner/mission-reservations.d.ts.map +0 -1
  388. package/dist/src/runner/mission-reservations.js +0 -116
  389. package/dist/src/runner/mission-reservations.js.map +0 -1
  390. package/dist/src/runner/mission-resolution-loop.d.ts +0 -172
  391. package/dist/src/runner/mission-resolution-loop.d.ts.map +0 -1
  392. package/dist/src/runner/mission-resolution-loop.js +0 -318
  393. package/dist/src/runner/mission-resolution-loop.js.map +0 -1
  394. package/dist/src/runner/mission-runtime-owner.d.ts +0 -36
  395. package/dist/src/runner/mission-runtime-owner.d.ts.map +0 -1
  396. package/dist/src/runner/mission-runtime-owner.js +0 -174
  397. package/dist/src/runner/mission-runtime-owner.js.map +0 -1
  398. package/dist/src/runner/mission-safe-executor.d.ts +0 -96
  399. package/dist/src/runner/mission-safe-executor.d.ts.map +0 -1
  400. package/dist/src/runner/mission-safe-executor.js +0 -288
  401. package/dist/src/runner/mission-safe-executor.js.map +0 -1
  402. package/dist/src/runner/mission-sandbox.d.ts +0 -16
  403. package/dist/src/runner/mission-sandbox.d.ts.map +0 -1
  404. package/dist/src/runner/mission-sandbox.js +0 -91
  405. package/dist/src/runner/mission-sandbox.js.map +0 -1
  406. package/dist/src/runner/mission-scheduler.d.ts +0 -57
  407. package/dist/src/runner/mission-scheduler.d.ts.map +0 -1
  408. package/dist/src/runner/mission-scheduler.js +0 -205
  409. package/dist/src/runner/mission-scheduler.js.map +0 -1
  410. package/dist/src/runner/mission-scope-expansion.d.ts +0 -47
  411. package/dist/src/runner/mission-scope-expansion.d.ts.map +0 -1
  412. package/dist/src/runner/mission-scope-expansion.js +0 -147
  413. package/dist/src/runner/mission-scope-expansion.js.map +0 -1
  414. package/dist/src/runner/mission-state-machine.d.ts +0 -118
  415. package/dist/src/runner/mission-state-machine.d.ts.map +0 -1
  416. package/dist/src/runner/mission-state-machine.js +0 -316
  417. package/dist/src/runner/mission-state-machine.js.map +0 -1
  418. package/dist/src/runner/mission-state-store.d.ts +0 -70
  419. package/dist/src/runner/mission-state-store.d.ts.map +0 -1
  420. package/dist/src/runner/mission-state-store.js +0 -916
  421. package/dist/src/runner/mission-state-store.js.map +0 -1
  422. package/dist/src/runner/mobile-device-lease.d.ts +0 -11
  423. package/dist/src/runner/mobile-device-lease.d.ts.map +0 -1
  424. package/dist/src/runner/mobile-device-lease.js +0 -121
  425. package/dist/src/runner/mobile-device-lease.js.map +0 -1
  426. package/dist/src/runner/mobile-visual-proof-command.d.ts +0 -17
  427. package/dist/src/runner/mobile-visual-proof-command.d.ts.map +0 -1
  428. package/dist/src/runner/mobile-visual-proof-command.js +0 -168
  429. package/dist/src/runner/mobile-visual-proof-command.js.map +0 -1
  430. package/dist/src/runner/plan-auto-command.d.ts +0 -35
  431. package/dist/src/runner/plan-auto-command.d.ts.map +0 -1
  432. package/dist/src/runner/plan-auto-command.js +0 -918
  433. package/dist/src/runner/plan-auto-command.js.map +0 -1
  434. package/dist/src/runner/plan-auto-recovery.d.ts +0 -83
  435. package/dist/src/runner/plan-auto-recovery.d.ts.map +0 -1
  436. package/dist/src/runner/plan-auto-recovery.js +0 -303
  437. package/dist/src/runner/plan-auto-recovery.js.map +0 -1
  438. package/dist/src/runner/prompt.d.ts +0 -68
  439. package/dist/src/runner/prompt.d.ts.map +0 -1
  440. package/dist/src/runner/prompt.js +0 -202
  441. package/dist/src/runner/prompt.js.map +0 -1
  442. package/dist/src/runner/proof-routing.d.ts +0 -73
  443. package/dist/src/runner/proof-routing.d.ts.map +0 -1
  444. package/dist/src/runner/proof-routing.js +0 -182
  445. package/dist/src/runner/proof-routing.js.map +0 -1
  446. package/dist/src/runner/proof-strategy.d.ts +0 -21
  447. package/dist/src/runner/proof-strategy.d.ts.map +0 -1
  448. package/dist/src/runner/proof-strategy.js +0 -26
  449. package/dist/src/runner/proof-strategy.js.map +0 -1
  450. package/dist/src/runner/recovery.d.ts +0 -25
  451. package/dist/src/runner/recovery.d.ts.map +0 -1
  452. package/dist/src/runner/recovery.js +0 -89
  453. package/dist/src/runner/recovery.js.map +0 -1
  454. package/dist/src/runner/review-gate-policy.d.ts +0 -13
  455. package/dist/src/runner/review-gate-policy.d.ts.map +0 -1
  456. package/dist/src/runner/review-gate-policy.js +0 -241
  457. package/dist/src/runner/review-gate-policy.js.map +0 -1
  458. package/dist/src/runner/review-gates.d.ts +0 -28
  459. package/dist/src/runner/review-gates.d.ts.map +0 -1
  460. package/dist/src/runner/review-gates.js +0 -328
  461. package/dist/src/runner/review-gates.js.map +0 -1
  462. package/dist/src/runner/rework-policy.d.ts +0 -48
  463. package/dist/src/runner/rework-policy.d.ts.map +0 -1
  464. package/dist/src/runner/rework-policy.js +0 -150
  465. package/dist/src/runner/rework-policy.js.map +0 -1
  466. package/dist/src/runner/run-log.d.ts +0 -18
  467. package/dist/src/runner/run-log.d.ts.map +0 -1
  468. package/dist/src/runner/run-log.js +0 -53
  469. package/dist/src/runner/run-log.js.map +0 -1
  470. package/dist/src/runner/runner-handoff-decision.d.ts +0 -48
  471. package/dist/src/runner/runner-handoff-decision.d.ts.map +0 -1
  472. package/dist/src/runner/runner-handoff-decision.js +0 -93
  473. package/dist/src/runner/runner-handoff-decision.js.map +0 -1
  474. package/dist/src/runner/safety.d.ts +0 -14
  475. package/dist/src/runner/safety.d.ts.map +0 -1
  476. package/dist/src/runner/safety.js +0 -31
  477. package/dist/src/runner/safety.js.map +0 -1
  478. package/dist/src/runner/scope-isolation-policy.d.ts +0 -17
  479. package/dist/src/runner/scope-isolation-policy.d.ts.map +0 -1
  480. package/dist/src/runner/scope-isolation-policy.js +0 -82
  481. package/dist/src/runner/scope-isolation-policy.js.map +0 -1
  482. package/dist/src/runner/scoped-auto-command.d.ts +0 -90
  483. package/dist/src/runner/scoped-auto-command.d.ts.map +0 -1
  484. package/dist/src/runner/scoped-auto-command.js +0 -561
  485. package/dist/src/runner/scoped-auto-command.js.map +0 -1
  486. package/dist/src/runner/scoped-recovery.d.ts +0 -78
  487. package/dist/src/runner/scoped-recovery.d.ts.map +0 -1
  488. package/dist/src/runner/scoped-recovery.js +0 -601
  489. package/dist/src/runner/scoped-recovery.js.map +0 -1
  490. package/dist/src/runner/session-home.d.ts +0 -8
  491. package/dist/src/runner/session-home.d.ts.map +0 -1
  492. package/dist/src/runner/session-home.js +0 -18
  493. package/dist/src/runner/session-home.js.map +0 -1
  494. package/dist/src/runner/status-command.d.ts +0 -38
  495. package/dist/src/runner/status-command.d.ts.map +0 -1
  496. package/dist/src/runner/status-command.js +0 -97
  497. package/dist/src/runner/status-command.js.map +0 -1
  498. package/dist/src/runner/target-activity-fence.d.ts.map +0 -1
  499. package/dist/src/runner/target-activity-fence.js.map +0 -1
  500. package/dist/src/runner/terminal-outcome.d.ts +0 -58
  501. package/dist/src/runner/terminal-outcome.d.ts.map +0 -1
  502. package/dist/src/runner/terminal-outcome.js +0 -73
  503. package/dist/src/runner/terminal-outcome.js.map +0 -1
  504. package/dist/src/runner/visual-proof-runner.d.ts +0 -27
  505. package/dist/src/runner/visual-proof-runner.d.ts.map +0 -1
  506. package/dist/src/runner/visual-proof-runner.js +0 -379
  507. package/dist/src/runner/visual-proof-runner.js.map +0 -1
  508. package/dist/src/runner/worktree-cleanup.d.ts +0 -27
  509. package/dist/src/runner/worktree-cleanup.d.ts.map +0 -1
  510. package/dist/src/runner/worktree-cleanup.js +0 -63
  511. package/dist/src/runner/worktree-cleanup.js.map +0 -1
  512. package/dist/src/setup/codex-command-resolver.d.ts +0 -10
  513. package/dist/src/setup/codex-command-resolver.d.ts.map +0 -1
  514. package/dist/src/setup/codex-command-resolver.js +0 -91
  515. package/dist/src/setup/codex-command-resolver.js.map +0 -1
  516. package/dist/src/setup/github-label-adapter.d.ts +0 -13
  517. package/dist/src/setup/github-label-adapter.d.ts.map +0 -1
  518. package/dist/src/setup/github-label-adapter.js +0 -28
  519. package/dist/src/setup/github-label-adapter.js.map +0 -1
  520. package/dist/src/setup/labels.d.ts +0 -27
  521. package/dist/src/setup/labels.d.ts.map +0 -1
  522. package/dist/src/setup/labels.js +0 -42
  523. package/dist/src/setup/labels.js.map +0 -1
  524. package/dist/src/setup/project-config.d.ts +0 -24
  525. package/dist/src/setup/project-config.d.ts.map +0 -1
  526. package/dist/src/setup/project-config.js +0 -678
  527. package/dist/src/setup/project-config.js.map +0 -1
  528. package/dist/src/setup/prompt-sync.d.ts +0 -14
  529. package/dist/src/setup/prompt-sync.d.ts.map +0 -1
  530. package/dist/src/setup/prompt-sync.js +0 -228
  531. package/dist/src/setup/prompt-sync.js.map +0 -1
  532. package/dist/src/setup/setup-command.d.ts +0 -29
  533. package/dist/src/setup/setup-command.d.ts.map +0 -1
  534. package/dist/src/setup/setup-command.js +0 -304
  535. package/dist/src/setup/setup-command.js.map +0 -1
  536. package/dist/src/setup/skill-runtime-v2-preparation.d.ts +0 -56
  537. package/dist/src/setup/skill-runtime-v2-preparation.d.ts.map +0 -1
  538. package/dist/src/setup/skill-runtime-v2-preparation.js +0 -304
  539. package/dist/src/setup/skill-runtime-v2-preparation.js.map +0 -1
  540. package/dist/src/setup/workflows.d.ts +0 -10
  541. package/dist/src/setup/workflows.d.ts.map +0 -1
  542. package/dist/src/setup/workflows.js +0 -41
  543. package/dist/src/setup/workflows.js.map +0 -1
  544. package/prompts/setup-skill.md +0 -12
  545. package/prompts/workflows/acceptance-proof.md +0 -29
  546. package/prompts/workflows/breakdown-review.md +0 -48
  547. package/prompts/workflows/issue-breakdown.md +0 -155
  548. package/prompts/workflows/issue-tree-orchestration.md +0 -163
  549. package/prompts/workflows/prd.md +0 -80
  550. package/prompts/workflows/scoped-implementation.md +0 -129
  551. package/prompts/workflows/triage.md +0 -149
  552. /package/dist/src/{process → v2/adapters}/command.d.ts +0 -0
  553. /package/dist/src/{process → v2/adapters}/command.js +0 -0
  554. /package/dist/src/{fs → v2/adapters}/durable-atomic-file.d.ts +0 -0
  555. /package/dist/src/{fs → v2/adapters}/durable-atomic-file.js +0 -0
  556. /package/dist/src/{github → v2/adapters}/gh-cli.d.ts +0 -0
  557. /package/dist/src/{github → v2/adapters}/gh-cli.js +0 -0
  558. /package/dist/src/{github → v2/adapters}/gh-issue-adapter.d.ts +0 -0
  559. /package/dist/src/{github → v2/adapters}/gh-issue-adapter.js +0 -0
  560. /package/dist/src/{github → v2/adapters}/gh-pull-request-adapter.d.ts +0 -0
  561. /package/dist/src/{github → v2/adapters}/gh-pull-request-adapter.js +0 -0
  562. /package/dist/src/{github → v2/adapters}/issues.d.ts +0 -0
  563. /package/dist/src/{github → v2/adapters}/issues.js +0 -0
  564. /package/dist/src/{runner → v2/adapters}/mission-coordinator-lock.d.ts +0 -0
  565. /package/dist/src/{runner → v2/adapters}/mission-coordinator-lock.js +0 -0
  566. /package/dist/src/{path-policy.d.ts → v2/adapters/path-policy.d.ts} +0 -0
  567. /package/dist/src/{path-policy.js → v2/adapters/path-policy.js} +0 -0
  568. /package/dist/src/{github → v2/adapters}/pull-requests.d.ts +0 -0
  569. /package/dist/src/{github → v2/adapters}/pull-requests.js +0 -0
  570. /package/dist/src/{runner → v2/adapters}/target-activity-fence.d.ts +0 -0
@@ -1,1001 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { publicationAttemptId } from './mission-identifiers.js';
3
- import { transitionPlanParent } from './mission-plan-parent.js';
4
- import { transitionMission } from './mission-state-machine.js';
5
- export const publicationStates = [
6
- 'prepared', 'push-intent', 'pushed', 'pr-create-intent', 'pr-confirmed',
7
- 'labels-intent', 'labels-confirmed', 'comment-intent', 'resumable',
8
- 'review-ready', 'external-input-required', 'safety-stop', 'cancelled',
9
- ];
10
- export class MissionPublicationCoordinator {
11
- store;
12
- constructor(store) {
13
- this.store = store;
14
- }
15
- prepare(input) {
16
- assertMissionPublicationRecord(input.publication);
17
- return this.store.mutate(input.expectedGeneration, (draft) => {
18
- const mission = draft.missions[input.missionId];
19
- if (!mission || mission.revision !== input.expectedRevision
20
- || mission.fencingEpoch !== input.fencingEpoch) {
21
- throw new Error(`Scoped Mission ${input.missionId} publication fence does not match.`);
22
- }
23
- if (mission.state !== 'candidate-ready') {
24
- throw new Error(`Scoped Mission ${input.missionId} is not candidate-ready.`);
25
- }
26
- if (draft.publications[input.publication.id]
27
- || Object.values(draft.publications).some((aggregate) => aggregate.value.ownerId === input.missionId)) {
28
- throw new Error(`Scoped Mission ${input.missionId} already has a Publication.`);
29
- }
30
- const receipt = mission.applyReceipt;
31
- if (!receipt
32
- || input.publication.ownerId !== mission.id
33
- || input.publication.fencingEpoch !== input.fencingEpoch
34
- || input.publication.candidateCommit !== receipt.commitSha
35
- || input.publication.candidateTree !== receipt.treeSha
36
- || input.publication.baseSha !== receipt.oldCommitSha
37
- || receipt.targetRef !== `refs/heads/${input.publication.branch}`) {
38
- throw new Error('Scoped Mission Publication does not match the applied candidate.');
39
- }
40
- draft.missions[input.missionId] = transitionMission(mission, {
41
- type: 'adapt-to-publication',
42
- });
43
- draft.publications[input.publication.id] = {
44
- revision: input.publication.revision,
45
- value: structuredClone(input.publication),
46
- };
47
- });
48
- }
49
- }
50
- export class MissionPublicationSaga {
51
- store;
52
- adapters;
53
- constructor(store, adapters) {
54
- this.store = store;
55
- this.adapters = adapters;
56
- }
57
- run(publicationId) {
58
- return this.runFromBranch(publicationId);
59
- }
60
- async defer(input) {
61
- const { snapshot, record } = await this.load(input.publicationId);
62
- if (hasScheduledPublicationRecovery(record)) {
63
- throw new Error(`Publication ${record.id} already has scheduled recovery.`);
64
- }
65
- if (record.state === 'resumable') {
66
- throw new Error(`Publication ${record.id} is already resumable.`);
67
- }
68
- return this.persist(snapshot, record, transitionMissionPublication(record, {
69
- type: 'transient-failure',
70
- nextEligibleAt: input.nextEligibleAt,
71
- actionKey: input.actionKey,
72
- }));
73
- }
74
- async resume(input) {
75
- const { snapshot, record } = await this.load(input.publicationId);
76
- if (hasScheduledPublicationRecovery(record)) {
77
- exactTimestamp(input.now);
78
- if (input.now < record.nextEligibleAt)
79
- throw new Error('Publication is not eligible.');
80
- return snapshot;
81
- }
82
- return this.persist(snapshot, record, transitionMissionPublication(record, {
83
- type: 'resume-eligible', now: input.now,
84
- }));
85
- }
86
- async cancel(publicationId) {
87
- const { snapshot, record } = await this.load(publicationId);
88
- return this.persist(snapshot, record, transitionMissionPublication(record, {
89
- type: 'cancel-requested',
90
- }));
91
- }
92
- async runFromBranch(publicationId) {
93
- await this.reconcileBranch(publicationId);
94
- await this.reconcilePullRequest(publicationId);
95
- await this.reconcileLabels(publicationId);
96
- return this.reconcileComment(publicationId);
97
- }
98
- reconcileBranch(publicationId) {
99
- return this.withRecovery(publicationId, () => this.reconcileBranchOnce(publicationId));
100
- }
101
- async reconcileBranchOnce(publicationId) {
102
- let { snapshot, record } = await this.load(publicationId);
103
- if (record.state !== 'prepared' && record.state !== 'push-intent')
104
- return snapshot;
105
- let observation = await this.remote('publication:observe-branch', () => this.adapters.branches.observe(record.branch));
106
- if (record.state === 'prepared') {
107
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
108
- type: 'branch-observed',
109
- observation: observation.kind === 'absent'
110
- ? observation
111
- : { kind: 'expected', commitSha: observation.commitSha },
112
- }));
113
- record = publicationFrom(snapshot, publicationId);
114
- if (record.state !== 'push-intent')
115
- return snapshot;
116
- observation = await this.remote('publication:observe-branch', () => this.adapters.branches.observe(record.branch));
117
- }
118
- if (observation.kind === 'absent') {
119
- const actionKey = mutationActionKey(record, 'push');
120
- if (record.mutationAttempt)
121
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
122
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
123
- type: 'mutation-attempted', attempt: { kind: 'push', actionKey },
124
- }));
125
- record = publicationFrom(snapshot, publicationId);
126
- await this.fence(record, actionKey);
127
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
128
- type: 'mutation-dispatched', actionKey,
129
- }));
130
- record = publicationFrom(snapshot, publicationId);
131
- await this.remote(actionKey, () => this.adapters.branches.push({
132
- branch: record.branch,
133
- candidateCommit: record.candidateCommit,
134
- }));
135
- observation = await this.remote('publication:observe-branch', () => this.adapters.branches.observe(record.branch));
136
- if (observation.kind === 'absent')
137
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
138
- }
139
- return this.persist(snapshot, record, transitionMissionPublication(record, {
140
- type: 'branch-observed',
141
- observation: { kind: 'expected', commitSha: observation.commitSha },
142
- }));
143
- }
144
- reconcilePullRequest(publicationId) {
145
- return this.withRecovery(publicationId, () => this.reconcilePullRequestOnce(publicationId));
146
- }
147
- async reconcilePullRequestOnce(publicationId) {
148
- let { snapshot, record } = await this.load(publicationId);
149
- if (record.state !== 'pushed' && record.state !== 'pr-create-intent')
150
- return snapshot;
151
- if (record.state === 'pr-create-intent') {
152
- const verified = await this.verifyBranchAndBase(snapshot, record);
153
- snapshot = verified.snapshot;
154
- record = verified.record;
155
- if (!verified.ok)
156
- return snapshot;
157
- }
158
- if (record.state === 'pushed' && record.baseObservedSha === undefined) {
159
- const baseCommit = await this.remote('publication:observe-base', () => this.adapters.branches.observeBase(record.baseBranch));
160
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
161
- type: 'base-observed',
162
- observation: baseCommit === undefined
163
- ? { kind: 'absent' }
164
- : { kind: 'present', commitSha: baseCommit },
165
- }));
166
- record = publicationFrom(snapshot, publicationId);
167
- if (record.state !== 'pushed')
168
- return snapshot;
169
- }
170
- let pullRequests = await this.remote('publication:list-pull-requests', () => this.adapters.pullRequests.listAllByHeadBranch(record.branch));
171
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
172
- type: 'pull-requests-observed', pullRequests,
173
- }));
174
- record = publicationFrom(snapshot, publicationId);
175
- if (record.state !== 'pr-create-intent')
176
- return snapshot;
177
- const verified = await this.verifyBranchAndBase(snapshot, record);
178
- snapshot = verified.snapshot;
179
- record = verified.record;
180
- if (!verified.ok)
181
- return snapshot;
182
- const marked = pullRequests.filter((pullRequest) => pullRequest.body.includes(record.marker));
183
- if (marked.length === 0) {
184
- const actionKey = mutationActionKey(record, 'create-pr');
185
- if (record.mutationAttempt)
186
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
187
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
188
- type: 'mutation-attempted', attempt: { kind: 'create-pr', actionKey },
189
- }));
190
- record = publicationFrom(snapshot, publicationId);
191
- await this.fence(record, actionKey);
192
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
193
- type: 'mutation-dispatched', actionKey,
194
- }));
195
- record = publicationFrom(snapshot, publicationId);
196
- await this.remote(actionKey, () => this.adapters.pullRequests.createDraftPullRequest({
197
- headBranch: record.branch,
198
- baseBranch: record.baseBranch,
199
- title: record.title,
200
- body: record.body,
201
- }));
202
- pullRequests = await this.remote('publication:list-pull-requests', () => this.adapters.pullRequests.listAllByHeadBranch(record.branch));
203
- if (!pullRequests.some((pullRequest) => pullRequest.body.includes(record.marker))) {
204
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
205
- }
206
- return this.persist(snapshot, record, transitionMissionPublication(record, {
207
- type: 'pull-requests-observed', pullRequests,
208
- }));
209
- }
210
- return snapshot;
211
- }
212
- async verifyBranchAndBase(snapshot, record) {
213
- const branch = await this.remote('publication:verify-branch', () => this.adapters.branches.observe(record.branch));
214
- if (branch.kind === 'absent' || branch.commitSha !== record.candidateCommit) {
215
- const stopped = await this.persist(snapshot, record, transitionMissionPublication(record, {
216
- type: 'remote-safety-conflict',
217
- }));
218
- return { snapshot: stopped, record: publicationFrom(stopped, record.id), ok: false };
219
- }
220
- const baseCommit = await this.remote('publication:verify-base', () => this.adapters.branches.observeBase(record.baseBranch));
221
- if (baseCommit === undefined) {
222
- const stopped = await this.persist(snapshot, record, transitionMissionPublication(record, {
223
- type: 'remote-external-change',
224
- }));
225
- return { snapshot: stopped, record: publicationFrom(stopped, record.id), ok: false };
226
- }
227
- if (baseCommit !== record.baseObservedSha) {
228
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
229
- type: 'base-observed', observation: { kind: 'present', commitSha: baseCommit },
230
- }));
231
- record = publicationFrom(snapshot, record.id);
232
- }
233
- return { snapshot, record, ok: true };
234
- }
235
- async verifyConfirmedRemote(snapshot, record) {
236
- const branch = await this.verifyBranchAndBase(snapshot, record);
237
- if (!branch.ok)
238
- return branch;
239
- snapshot = branch.snapshot;
240
- record = branch.record;
241
- const pullRequests = await this.remote('publication:verify-pull-request', () => this.adapters.pullRequests.listAllByHeadBranch(record.branch));
242
- const marked = pullRequests.filter((pullRequest) => pullRequest.body.includes(record.marker));
243
- const expected = record.pullRequest;
244
- if (marked.length > 1 || !expected) {
245
- const stopped = await this.persist(snapshot, record, transitionMissionPublication(record, {
246
- type: 'remote-safety-conflict',
247
- }));
248
- return { snapshot: stopped, record: publicationFrom(stopped, record.id), ok: false };
249
- }
250
- if (marked.length === 1 && marked[0].nodeId !== expected.nodeId) {
251
- const event = maintainerAssociation(marked[0].authorAssociation)
252
- ? { type: 'remote-external-change' }
253
- : { type: 'remote-safety-conflict' };
254
- const stopped = await this.persist(snapshot, record, transitionMissionPublication(record, event));
255
- return { snapshot: stopped, record: publicationFrom(stopped, record.id), ok: false };
256
- }
257
- if (marked.length === 1 && (marked[0].headRefName !== record.branch
258
- || marked[0].baseRefName !== record.baseBranch)) {
259
- const stopped = await this.persist(snapshot, record, transitionMissionPublication(record, {
260
- type: 'remote-safety-conflict',
261
- }));
262
- return { snapshot: stopped, record: publicationFrom(stopped, record.id), ok: false };
263
- }
264
- if (marked.length === 0 || marked[0].state !== 'OPEN' || !marked[0].isDraft
265
- || digest(marked[0].title) !== record.titleHash
266
- || digest(marked[0].body) !== record.bodyHash) {
267
- const stopped = await this.persist(snapshot, record, transitionMissionPublication(record, {
268
- type: 'remote-external-change',
269
- }));
270
- return { snapshot: stopped, record: publicationFrom(stopped, record.id), ok: false };
271
- }
272
- return { snapshot, record, ok: true };
273
- }
274
- reconcileLabels(publicationId) {
275
- return this.withRecovery(publicationId, () => this.reconcileLabelsOnce(publicationId));
276
- }
277
- async reconcileLabelsOnce(publicationId) {
278
- let { snapshot, record } = await this.load(publicationId);
279
- if (record.state !== 'pr-confirmed' && record.state !== 'labels-intent')
280
- return snapshot;
281
- const verified = await this.verifyConfirmedRemote(snapshot, record);
282
- snapshot = verified.snapshot;
283
- record = verified.record;
284
- if (!verified.ok)
285
- return snapshot;
286
- const labels = await this.remote('publication:list-labels', () => this.adapters.issues.getLabels(record.issueNumber));
287
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
288
- type: 'labels-observed', labels,
289
- }));
290
- record = publicationFrom(snapshot, publicationId);
291
- if (record.state !== 'labels-intent' || !record.labelMutation)
292
- return snapshot;
293
- if (record.labelMutation.add.length > 0) {
294
- const actionKey = mutationActionKey(record, 'add-labels');
295
- if (record.mutationAttempt)
296
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
297
- const attemptedLabels = [...record.labelMutation.add];
298
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
299
- type: 'mutation-attempted', attempt: { kind: 'add-labels', actionKey, labels: attemptedLabels },
300
- }));
301
- record = publicationFrom(snapshot, publicationId);
302
- await this.fence(record, actionKey);
303
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
304
- type: 'mutation-dispatched', actionKey,
305
- }));
306
- record = publicationFrom(snapshot, publicationId);
307
- await this.remote(actionKey, () => this.adapters.issues.addLabels(record.issueNumber, attemptedLabels));
308
- const observed = await this.remote('publication:list-labels', () => this.adapters.issues.getLabels(record.issueNumber));
309
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
310
- type: 'labels-observed', labels: observed,
311
- }));
312
- record = publicationFrom(snapshot, publicationId);
313
- if (record.mutationAttempt)
314
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
315
- }
316
- if (record.state === 'labels-intent' && record.labelMutation?.remove.length) {
317
- const actionKey = mutationActionKey(record, 'remove-labels');
318
- if (record.mutationAttempt)
319
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
320
- const attemptedLabels = [...record.labelMutation.remove];
321
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
322
- type: 'mutation-attempted', attempt: { kind: 'remove-labels', actionKey, labels: attemptedLabels },
323
- }));
324
- record = publicationFrom(snapshot, publicationId);
325
- await this.fence(record, actionKey);
326
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
327
- type: 'mutation-dispatched', actionKey,
328
- }));
329
- record = publicationFrom(snapshot, publicationId);
330
- await this.remote(actionKey, () => this.adapters.issues.removeLabels(record.issueNumber, attemptedLabels));
331
- }
332
- const observed = await this.remote('publication:list-labels', () => this.adapters.issues.getLabels(record.issueNumber));
333
- if (record.mutationAttempt && !labelAttemptSatisfied(record.mutationAttempt, observed)) {
334
- throw new MissionPublicationBoundaryError(record.mutationAttempt.actionKey, 'transient', undefined, true);
335
- }
336
- return this.persist(snapshot, record, transitionMissionPublication(record, {
337
- type: 'labels-observed', labels: observed,
338
- }));
339
- }
340
- reconcileComment(publicationId) {
341
- return this.withRecovery(publicationId, () => this.reconcileCommentOnce(publicationId));
342
- }
343
- async reconcileCommentOnce(publicationId) {
344
- let { snapshot, record } = await this.load(publicationId);
345
- if (record.state !== 'labels-confirmed' && record.state !== 'comment-intent')
346
- return snapshot;
347
- const verified = await this.verifyConfirmedRemote(snapshot, record);
348
- snapshot = verified.snapshot;
349
- record = verified.record;
350
- if (!verified.ok)
351
- return snapshot;
352
- let comments = await this.remote('publication:list-comments', () => this.adapters.issues.listAllComments(record.issueNumber));
353
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
354
- type: 'comments-observed', comments,
355
- }));
356
- record = publicationFrom(snapshot, publicationId);
357
- if (record.state !== 'comment-intent')
358
- return snapshot;
359
- const marker = record.marker.replace(':publication ', ':publication-comment ');
360
- if (!comments.some((comment) => comment.body.includes(marker))) {
361
- const actionKey = mutationActionKey(record, 'post-comment');
362
- if (record.mutationAttempt)
363
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
364
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
365
- type: 'mutation-attempted', attempt: { kind: 'post-comment', actionKey },
366
- }));
367
- record = publicationFrom(snapshot, publicationId);
368
- await this.fence(record, actionKey);
369
- snapshot = await this.persist(snapshot, record, transitionMissionPublication(record, {
370
- type: 'mutation-dispatched', actionKey,
371
- }));
372
- record = publicationFrom(snapshot, publicationId);
373
- await this.remote(actionKey, () => this.adapters.issues.postComment(record.issueNumber, record.terminalComment));
374
- comments = await this.remote('publication:list-comments', () => this.adapters.issues.listAllComments(record.issueNumber));
375
- if (!comments.some((comment) => comment.body.includes(marker))) {
376
- throw new MissionPublicationBoundaryError(actionKey, 'transient', undefined, true);
377
- }
378
- return this.persist(snapshot, record, transitionMissionPublication(record, {
379
- type: 'comments-observed', comments,
380
- }));
381
- }
382
- return snapshot;
383
- }
384
- async withRecovery(publicationId, operation) {
385
- try {
386
- return await operation();
387
- }
388
- catch (error) {
389
- if (!(error instanceof MissionPublicationBoundaryError)
390
- && !(error instanceof Error && /(?:generation|revision) conflict/u.test(error.message))) {
391
- throw error;
392
- }
393
- for (let attempt = 0; attempt < 4; attempt += 1) {
394
- const { snapshot, record } = await this.load(publicationId);
395
- if (terminal(record.state) || record.state === 'resumable')
396
- return snapshot;
397
- let next;
398
- if (error instanceof MissionPublicationBoundaryError && error.classification === 'authority') {
399
- next = transitionMissionPublication(record, { type: 'authority-missing' });
400
- }
401
- else {
402
- const now = this.adapters.now?.() ?? new Date().toISOString();
403
- exactTimestamp(now);
404
- const delay = this.adapters.retryDelayMs ?? 1_000;
405
- if (!Number.isSafeInteger(delay) || delay <= 0)
406
- throw new Error('Publication retry delay must be positive.');
407
- const actionKey = error instanceof MissionPublicationBoundaryError
408
- ? error.actionKey : `publication:${record.id}:state-conflict`;
409
- next = transitionMissionPublication(record, {
410
- type: 'transient-failure',
411
- nextEligibleAt: new Date(Date.parse(now) + delay).toISOString(),
412
- actionKey,
413
- postconditionObserved: error instanceof MissionPublicationBoundaryError
414
- && error.postconditionObserved,
415
- });
416
- }
417
- try {
418
- return await this.persist(snapshot, record, next);
419
- }
420
- catch (persistError) {
421
- if (!(persistError instanceof Error && /(?:generation|revision) conflict/u.test(persistError.message))) {
422
- throw persistError;
423
- }
424
- }
425
- }
426
- throw new Error(`Publication ${publicationId} could not persist recovery after 4 conflicts.`, {
427
- cause: error,
428
- });
429
- }
430
- }
431
- async remote(actionKey, operation) {
432
- try {
433
- return await operation();
434
- }
435
- catch (error) {
436
- if (error instanceof MissionPublicationBoundaryError)
437
- throw error;
438
- const classification = this.adapters.classifyRemoteError?.(error) ?? defaultRemoteClassification(error);
439
- throw new MissionPublicationBoundaryError(actionKey, classification, error);
440
- }
441
- }
442
- fence(record, actionKey) {
443
- return this.remote(actionKey, () => this.adapters.assertMutationFence({
444
- publicationId: record.id,
445
- ownerId: record.ownerId,
446
- repository: record.repository,
447
- fencingEpoch: record.fencingEpoch,
448
- actionKey,
449
- }));
450
- }
451
- async load(publicationId) {
452
- const snapshot = await this.store.load();
453
- return { snapshot, record: publicationFrom(snapshot, publicationId) };
454
- }
455
- persist(snapshot, previous, next) {
456
- return this.store.mutate(snapshot.generation, (draft) => {
457
- const stored = draft.publications[previous.id];
458
- if (!stored || stored.revision !== previous.revision) {
459
- throw new Error(`Publication ${previous.id} revision conflict.`);
460
- }
461
- draft.publications[previous.id] = {
462
- revision: next.revision,
463
- value: structuredClone(next),
464
- };
465
- delete draft.nextEligibleAt[publicationScheduleKey(previous.id)];
466
- if ((next.state === 'resumable' || hasScheduledPublicationRecovery(next)) && next.nextEligibleAt) {
467
- draft.nextEligibleAt[publicationScheduleKey(next.id)] = next.nextEligibleAt;
468
- }
469
- const owner = draft.missions[next.ownerId];
470
- if (owner) {
471
- let mapped = owner;
472
- if (next.state === 'resumable' && previous.state !== 'resumable') {
473
- const deferred = transitionMission(mapped, {
474
- type: 'publication-transient-failure',
475
- actionKey: next.actionKey,
476
- nextEligibleAt: next.nextEligibleAt,
477
- });
478
- mapped = {
479
- ...deferred,
480
- resumableReason: 'Publication remote boundary is transient or not yet observable.',
481
- requiredPredicate: 'Publication remote postcondition becomes observable.',
482
- };
483
- }
484
- else if (previous.state === 'resumable' && next.state !== 'resumable'
485
- && mapped.state === 'resumable') {
486
- mapped = transitionMission(mapped, {
487
- type: 'resume-eligible', now: previous.nextEligibleAt,
488
- });
489
- }
490
- if (terminal(next.state) && next.state !== previous.state) {
491
- mapped = transitionMission(mapped, { type: publicationOwnerEvent(next.state) });
492
- }
493
- draft.missions[next.ownerId] = mapped;
494
- }
495
- const parent = draft.planParents[next.ownerId];
496
- if (parent) {
497
- let mapped = parent;
498
- if (next.state === 'resumable' && previous.state !== 'resumable') {
499
- mapped = transitionPlanParent(mapped, {
500
- type: 'transient-failure',
501
- resumeTarget: 'publication-prepared',
502
- actionKey: next.actionKey,
503
- nextEligibleAt: next.nextEligibleAt,
504
- reason: 'Publication remote boundary is transient or not yet observable.',
505
- requiredPredicate: 'Publication remote postcondition becomes observable.',
506
- });
507
- }
508
- else if (previous.state === 'resumable' && next.state !== 'resumable'
509
- && mapped.state === 'wave-waiting') {
510
- mapped = transitionPlanParent(mapped, {
511
- type: 'resume-eligible', now: previous.nextEligibleAt,
512
- });
513
- }
514
- if (terminal(next.state) && next.state !== previous.state) {
515
- mapped = transitionPlanParent(mapped, next.state === 'review-ready' ? { type: 'publication-review-ready' }
516
- : next.state === 'external-input-required' ? { type: 'external-input-required' }
517
- : next.state === 'safety-stop' ? { type: 'safety-stop' }
518
- : { type: 'publication-cancelled' });
519
- }
520
- draft.planParents[next.ownerId] = mapped;
521
- }
522
- });
523
- }
524
- }
525
- export function createMissionPublication(input) {
526
- const id = publicationAttemptId({
527
- ownerId: input.ownerId,
528
- candidateCommit: input.candidateCommit,
529
- baseSha: input.baseSha,
530
- configHash: input.configHash,
531
- });
532
- return {
533
- ...structuredClone(input), id, revision: 1, state: 'prepared',
534
- titleHash: digest(input.title), bodyHash: digest(input.body),
535
- commentHash: digest(input.terminalComment), warnings: [],
536
- };
537
- }
538
- export function publicationScheduleKey(publicationId) {
539
- requireString(publicationId, 'Publication schedule ID');
540
- return `publication:${publicationId}`;
541
- }
542
- export function listEligiblePublications(snapshot, now, limit = 100) {
543
- exactTimestamp(now);
544
- if (!Number.isSafeInteger(limit) || limit <= 0)
545
- throw new Error('Publication scheduler limit must be positive.');
546
- const eligible = [];
547
- for (const [key, nextEligibleAt] of Object.entries(snapshot.nextEligibleAt)
548
- .filter(([key, value]) => key.startsWith('publication:') && value <= now)
549
- .sort((left, right) => left[1].localeCompare(right[1]) || left[0].localeCompare(right[0]))) {
550
- if (eligible.length >= Math.min(limit, 100))
551
- break;
552
- const publicationId = key.slice('publication:'.length);
553
- const aggregate = snapshot.publications[publicationId];
554
- const publication = aggregate?.value;
555
- if (!publication || (publication.state !== 'resumable' && !hasScheduledPublicationRecovery(publication))
556
- || publication.nextEligibleAt !== nextEligibleAt
557
- || !publication.actionKey)
558
- continue;
559
- eligible.push({
560
- publicationId,
561
- revision: publication.revision,
562
- nextEligibleAt,
563
- actionKey: publication.actionKey,
564
- });
565
- }
566
- return eligible;
567
- }
568
- export function hasScheduledPublicationRecovery(record) {
569
- return record.state !== 'resumable'
570
- && record.resumeTarget === record.state
571
- && record.nextEligibleAt !== undefined
572
- && record.actionKey !== undefined;
573
- }
574
- export function assertMissionPublicationRecord(value, path = 'Publication') {
575
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
576
- throw new Error(`${path} must be an object.`);
577
- }
578
- const record = value;
579
- const allowed = new Set([
580
- 'id', 'revision', 'state', 'ownerId', 'repository', 'issueNumber', 'fencingEpoch',
581
- 'candidateCommit', 'candidateTree', 'baseSha', 'validationSnapshot',
582
- 'validationReceiptIds', 'configHash', 'branch', 'baseBranch', 'marker', 'title',
583
- 'titleHash', 'body', 'bodyHash', 'managedLabels', 'desiredLabels', 'terminalComment',
584
- 'commentHash', 'baseObservedSha', 'warnings', 'pullRequest', 'labelMutation',
585
- 'mutationAttempt', 'resumeTarget', 'nextEligibleAt', 'actionKey',
586
- ]);
587
- for (const key of Object.keys(record)) {
588
- if (!allowed.has(key))
589
- throw new Error(`${path} has unexpected field ${key}.`);
590
- }
591
- for (const field of [
592
- 'id', 'ownerId', 'repository', 'branch', 'baseBranch', 'marker', 'title', 'body',
593
- 'terminalComment',
594
- ])
595
- requireString(record[field], `${path}.${field}`);
596
- requireInteger(record.revision, `${path}.revision`, 1);
597
- requireInteger(record.issueNumber, `${path}.issueNumber`, 1);
598
- requireInteger(record.fencingEpoch, `${path}.fencingEpoch`, 1);
599
- if (typeof record.state !== 'string' || !publicationStates.includes(record.state)) {
600
- throw new Error(`${path}.state is invalid.`);
601
- }
602
- for (const field of ['candidateCommit', 'candidateTree', 'baseSha', 'validationSnapshot']) {
603
- requireObjectId(record[field], `${path}.${field}`);
604
- }
605
- for (const field of ['configHash', 'titleHash', 'bodyHash', 'commentHash']) {
606
- requireDigest(record[field], `${path}.${field}`);
607
- }
608
- requireStringArray(record.validationReceiptIds, `${path}.validationReceiptIds`);
609
- requireStringArray(record.managedLabels, `${path}.managedLabels`);
610
- requireStringArray(record.desiredLabels, `${path}.desiredLabels`);
611
- requireStringArray(record.warnings, `${path}.warnings`);
612
- if (!record.desiredLabels.every((label) => record.managedLabels.includes(label))) {
613
- throw new Error(`${path}.desiredLabels must stay inside managedLabels.`);
614
- }
615
- if (!record.body.includes(record.marker)) {
616
- throw new Error(`${path}.body must contain the immutable publication marker.`);
617
- }
618
- const commentMarker = record.marker.replace(':publication ', ':publication-comment ');
619
- if (!record.terminalComment.includes(commentMarker)) {
620
- throw new Error(`${path}.terminalComment must contain the immutable comment marker.`);
621
- }
622
- if (record.titleHash !== digest(record.title)
623
- || record.bodyHash !== digest(record.body)
624
- || record.commentHash !== digest(record.terminalComment)) {
625
- throw new Error(`${path} content hashes do not match pinned content.`);
626
- }
627
- if (record.baseObservedSha !== undefined)
628
- requireObjectId(record.baseObservedSha, `${path}.baseObservedSha`);
629
- if (record.pullRequest !== undefined)
630
- assertPublicationPullRequest(record.pullRequest, `${path}.pullRequest`);
631
- if (record.labelMutation !== undefined) {
632
- const mutation = requireObject(record.labelMutation, `${path}.labelMutation`);
633
- exactFields(mutation, ['add', 'remove', 'preserve'], `${path}.labelMutation`);
634
- requireStringArray(mutation.add, `${path}.labelMutation.add`);
635
- requireStringArray(mutation.remove, `${path}.labelMutation.remove`);
636
- requireStringArray(mutation.preserve, `${path}.labelMutation.preserve`);
637
- }
638
- if (record.mutationAttempt !== undefined) {
639
- assertMutationAttempt(record.mutationAttempt, `${path}.mutationAttempt`);
640
- }
641
- if (record.resumeTarget !== undefined) {
642
- if (typeof record.resumeTarget !== 'string'
643
- || !publicationStates.includes(record.resumeTarget)
644
- || record.resumeTarget === 'resumable') {
645
- throw new Error(`${path}.resumeTarget is invalid.`);
646
- }
647
- }
648
- if (record.nextEligibleAt !== undefined)
649
- exactTimestampField(record.nextEligibleAt, `${path}.nextEligibleAt`);
650
- if (record.actionKey !== undefined)
651
- requireString(record.actionKey, `${path}.actionKey`);
652
- if (record.state === 'resumable'
653
- && (!record.resumeTarget || !record.nextEligibleAt || !record.actionKey)) {
654
- throw new Error(`${path} resumable state requires target, timestamp, and action key.`);
655
- }
656
- if (record.state !== 'resumable' && !hasScheduledPublicationRecovery(value)
657
- && (record.resumeTarget !== undefined || record.nextEligibleAt !== undefined || record.actionKey !== undefined)) {
658
- throw new Error(`${path} non-resumable state cannot retain scheduling metadata.`);
659
- }
660
- const effectiveState = record.state === 'resumable' ? record.resumeTarget : record.state;
661
- if (record.mutationAttempt !== undefined) {
662
- const attempt = record.mutationAttempt;
663
- if (!mutationMatchesState(attempt.kind, effectiveState)
664
- || attempt.actionKey !== mutationActionKey(value, attempt.kind)) {
665
- throw new Error(`${path}.mutationAttempt does not match its publication phase and identity.`);
666
- }
667
- }
668
- const requiresPullRequest = [
669
- 'pr-confirmed', 'labels-intent', 'labels-confirmed', 'comment-intent', 'review-ready',
670
- ].includes(effectiveState);
671
- if (requiresPullRequest && record.pullRequest === undefined) {
672
- throw new Error(`${path} ${String(record.state)} requires a pinned pull request.`);
673
- }
674
- if (record.pullRequest !== undefined) {
675
- const pullRequest = record.pullRequest;
676
- if (pullRequest.headRefName !== record.branch || pullRequest.baseRefName !== record.baseBranch
677
- || digest(pullRequest.title) !== record.titleHash || digest(pullRequest.body) !== record.bodyHash
678
- || !pullRequest.body.includes(record.marker)) {
679
- throw new Error(`${path}.pullRequest does not match pinned publication content.`);
680
- }
681
- }
682
- if (effectiveState === 'labels-intent' && record.labelMutation === undefined) {
683
- throw new Error(`${path} labels-intent requires labelMutation.`);
684
- }
685
- if (effectiveState !== 'labels-intent' && !terminal(record.state)
686
- && record.labelMutation !== undefined) {
687
- throw new Error(`${path}.labelMutation is allowed only in labels-intent.`);
688
- }
689
- if ([
690
- 'pr-create-intent', 'pr-confirmed', 'labels-intent', 'labels-confirmed', 'comment-intent',
691
- 'review-ready',
692
- ].includes(effectiveState) && record.baseObservedSha === undefined) {
693
- throw new Error(`${path} ${String(record.state)} requires an observed base identity.`);
694
- }
695
- const typed = value;
696
- const expectedId = publicationAttemptId({
697
- ownerId: typed.ownerId,
698
- candidateCommit: typed.candidateCommit,
699
- baseSha: typed.baseSha,
700
- configHash: typed.configHash,
701
- });
702
- if (typed.id !== expectedId)
703
- throw new Error(`${path}.id does not match its pinned identity.`);
704
- }
705
- export function transitionMissionPublication(record, event) {
706
- const next = structuredClone(record);
707
- if (hasScheduledPublicationRecovery(record) && event.type !== 'resume-eligible') {
708
- delete next.resumeTarget;
709
- delete next.nextEligibleAt;
710
- delete next.actionKey;
711
- }
712
- if (event.type === 'remote-safety-conflict' && !terminal(record.state)) {
713
- return terminalAdvance(next, 'safety-stop');
714
- }
715
- if (event.type === 'remote-external-change' && !terminal(record.state)) {
716
- return terminalAdvance(next, 'external-input-required');
717
- }
718
- if (event.type === 'cancel-requested' && !terminal(record.state))
719
- return terminalAdvance(next, 'cancelled');
720
- if (event.type === 'authority-missing' && !terminal(record.state))
721
- return terminalAdvance(next, 'external-input-required');
722
- if (event.type === 'transient-failure' && !terminal(record.state)) {
723
- exactTimestamp(event.nextEligibleAt);
724
- if (next.mutationAttempt?.stage === 'prepared')
725
- delete next.mutationAttempt;
726
- else if (next.mutationAttempt?.stage === 'dispatched' && event.postconditionObserved) {
727
- next.mutationAttempt.observationAttempts += 1;
728
- if (next.mutationAttempt.observationAttempts >= 3) {
729
- return terminalAdvance(next, 'external-input-required');
730
- }
731
- }
732
- next.resumeTarget = record.state === 'resumable' ? record.resumeTarget : record.state;
733
- next.nextEligibleAt = event.nextEligibleAt;
734
- next.actionKey = event.actionKey;
735
- return advance(next, 'resumable');
736
- }
737
- if (record.state === 'resumable' && event.type === 'resume-eligible') {
738
- exactTimestamp(event.now);
739
- if (!record.resumeTarget || !record.nextEligibleAt || event.now < record.nextEligibleAt)
740
- throw new Error('Publication is not eligible.');
741
- const target = record.resumeTarget;
742
- next.state = target;
743
- next.revision += 1;
744
- return next;
745
- }
746
- if (event.type === 'mutation-attempted' && !terminal(record.state) && record.state !== 'resumable') {
747
- if (record.mutationAttempt || !mutationMatchesState(event.attempt.kind, record.state)) {
748
- throw new Error(`Publication mutation attempt is invalid for ${record.state}.`);
749
- }
750
- const attempt = {
751
- ...structuredClone(event.attempt), stage: 'prepared', observationAttempts: 0,
752
- };
753
- assertMutationAttempt(attempt, 'Publication mutationAttempt');
754
- next.mutationAttempt = attempt;
755
- return advance(next, record.state);
756
- }
757
- if (event.type === 'mutation-dispatched' && record.mutationAttempt?.stage === 'prepared'
758
- && record.mutationAttempt.actionKey === event.actionKey) {
759
- next.mutationAttempt = { ...record.mutationAttempt, stage: 'dispatched', observationAttempts: 0 };
760
- return advance(next, record.state);
761
- }
762
- if ((record.state === 'prepared' || record.state === 'push-intent') && event.type === 'branch-observed') {
763
- if (event.observation.kind === 'other')
764
- return terminalAdvance(next, 'safety-stop');
765
- if (event.observation.kind === 'expected') {
766
- if (event.observation.commitSha !== record.candidateCommit)
767
- return terminalAdvance(next, 'safety-stop');
768
- delete next.mutationAttempt;
769
- return advance(next, 'pushed');
770
- }
771
- return advance(next, 'push-intent');
772
- }
773
- if (!terminal(record.state) && record.state !== 'prepared' && record.state !== 'push-intent'
774
- && record.state !== 'resumable' && event.type === 'base-observed') {
775
- if (event.observation.kind === 'absent')
776
- return terminalAdvance(next, 'external-input-required');
777
- next.baseObservedSha = event.observation.commitSha;
778
- if (event.observation.commitSha !== record.baseSha) {
779
- const warning = `Base branch advanced from ${record.baseSha} to ${event.observation.commitSha} after validation.`;
780
- if (!next.warnings.includes(warning))
781
- next.warnings.push(warning);
782
- }
783
- return advance(next, record.state);
784
- }
785
- if ((record.state === 'pushed' || record.state === 'pr-create-intent') && event.type === 'pull-requests-observed') {
786
- const marked = event.pullRequests.filter((pullRequest) => pullRequest.body.includes(record.marker));
787
- if (marked.length > 1)
788
- return terminalAdvance(next, 'safety-stop');
789
- if (marked.length === 0)
790
- return advance(next, 'pr-create-intent');
791
- const pullRequest = marked[0];
792
- const immutable = pullRequest.headRefName === record.branch && pullRequest.baseRefName === record.baseBranch;
793
- if (!immutable)
794
- return terminalAdvance(next, 'safety-stop');
795
- if (pullRequest.state !== 'OPEN' || !pullRequest.isDraft
796
- || digest(pullRequest.title) !== record.titleHash || digest(pullRequest.body) !== record.bodyHash) {
797
- return terminalAdvance(next, 'external-input-required');
798
- }
799
- next.pullRequest = structuredClone(pullRequest);
800
- delete next.mutationAttempt;
801
- return advance(next, 'pr-confirmed');
802
- }
803
- if ((record.state === 'pr-confirmed' || record.state === 'labels-intent') && event.type === 'labels-observed') {
804
- if (record.mutationAttempt
805
- && (record.mutationAttempt.kind === 'add-labels' || record.mutationAttempt.kind === 'remove-labels')) {
806
- if (!labelAttemptSatisfied(record.mutationAttempt, event.labels)) {
807
- return advance(next, 'labels-intent');
808
- }
809
- delete next.mutationAttempt;
810
- }
811
- const labels = new Set(event.labels);
812
- const desired = new Set(record.desiredLabels);
813
- const add = record.desiredLabels.filter((label) => !labels.has(label));
814
- const remove = record.managedLabels.filter((label) => labels.has(label) && !desired.has(label));
815
- const preserve = event.labels.filter((label) => !record.managedLabels.includes(label));
816
- if (add.length || remove.length) {
817
- next.labelMutation = { add: add.sort(), remove: remove.sort(), preserve: preserve.sort() };
818
- return advance(next, 'labels-intent');
819
- }
820
- delete next.labelMutation;
821
- return advance(next, 'labels-confirmed');
822
- }
823
- if ((record.state === 'labels-confirmed' || record.state === 'comment-intent') && event.type === 'comments-observed') {
824
- const markerComments = event.comments.filter((comment) => comment.body.includes(record.marker.replace(':publication ', ':publication-comment ')));
825
- const exact = markerComments.filter((comment) => digest(comment.body) === record.commentHash);
826
- if (markerComments.length === 0)
827
- return advance(next, 'comment-intent');
828
- if (markerComments.length === 1 && exact.length === 1) {
829
- delete next.mutationAttempt;
830
- return terminalAdvance(next, 'review-ready');
831
- }
832
- return terminalAdvance(next, 'external-input-required');
833
- }
834
- throw new Error(`Publication transition is not allowed: ${record.state} + ${event.type}`);
835
- }
836
- function advance(record, state) {
837
- record.state = state;
838
- record.revision += 1;
839
- return record;
840
- }
841
- function terminalAdvance(record, state) {
842
- delete record.resumeTarget;
843
- delete record.nextEligibleAt;
844
- delete record.actionKey;
845
- delete record.mutationAttempt;
846
- return advance(record, state);
847
- }
848
- function mutationActionKey(record, kind) {
849
- return `publication:${record.id}:${kind}`;
850
- }
851
- function mutationMatchesState(kind, state) {
852
- return (kind === 'push' && state === 'push-intent')
853
- || (kind === 'create-pr' && state === 'pr-create-intent')
854
- || ((kind === 'add-labels' || kind === 'remove-labels') && state === 'labels-intent')
855
- || (kind === 'post-comment' && state === 'comment-intent');
856
- }
857
- function labelAttemptSatisfied(attempt, labels) {
858
- const observed = new Set(labels);
859
- if (attempt.kind === 'add-labels')
860
- return (attempt.labels ?? []).every((label) => observed.has(label));
861
- if (attempt.kind === 'remove-labels')
862
- return (attempt.labels ?? []).every((label) => !observed.has(label));
863
- return false;
864
- }
865
- function maintainerAssociation(value) {
866
- return ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(value.toUpperCase());
867
- }
868
- function defaultRemoteClassification(error) {
869
- if (error && typeof error === 'object') {
870
- const value = error;
871
- const combined = `${String(value.status ?? '')} ${String(value.code ?? '')} ${String(value.stderr ?? '')} ${String(value.message ?? '')}`;
872
- if (/(?:^|\D)(?:401|403)(?:\D|$)|unauthori[sz]ed|forbidden/u.test(combined.toLowerCase()))
873
- return 'authority';
874
- }
875
- return 'transient';
876
- }
877
- class MissionPublicationBoundaryError extends Error {
878
- actionKey;
879
- classification;
880
- cause;
881
- postconditionObserved;
882
- constructor(actionKey, classification, cause, postconditionObserved = false) {
883
- super(`Publication remote boundary failed: ${actionKey}`);
884
- this.actionKey = actionKey;
885
- this.classification = classification;
886
- this.cause = cause;
887
- this.postconditionObserved = postconditionObserved;
888
- }
889
- }
890
- function terminal(state) {
891
- return ['review-ready', 'external-input-required', 'safety-stop', 'cancelled'].includes(state);
892
- }
893
- function digest(value) {
894
- return `sha256:${createHash('sha256').update(value, 'utf8').digest('hex')}`;
895
- }
896
- function exactTimestamp(value) {
897
- const milliseconds = Date.parse(value);
898
- if (!Number.isFinite(milliseconds) || new Date(milliseconds).toISOString() !== value)
899
- throw new Error('Publication timestamp is invalid.');
900
- }
901
- function exactTimestampField(value, path) {
902
- if (typeof value !== 'string')
903
- throw new Error(`${path} must be a timestamp.`);
904
- try {
905
- exactTimestamp(value);
906
- }
907
- catch {
908
- throw new Error(`${path} must be an exact UTC ISO timestamp.`);
909
- }
910
- }
911
- function requireObject(value, path) {
912
- if (!value || typeof value !== 'object' || Array.isArray(value))
913
- throw new Error(`${path} must be an object.`);
914
- return value;
915
- }
916
- function exactFields(record, fields, path) {
917
- const expected = new Set(fields);
918
- if (Object.keys(record).length !== expected.size || Object.keys(record).some((key) => !expected.has(key))) {
919
- throw new Error(`${path} must contain exact fields.`);
920
- }
921
- }
922
- function requireString(value, path) {
923
- if (typeof value !== 'string' || value.trim().length === 0)
924
- throw new Error(`${path} must be non-empty.`);
925
- }
926
- function requireInteger(value, path, minimum) {
927
- if (!Number.isSafeInteger(value) || value < minimum)
928
- throw new Error(`${path} must be an integer >= ${minimum}.`);
929
- }
930
- function requireObjectId(value, path) {
931
- if (typeof value !== 'string' || !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u.test(value)) {
932
- throw new Error(`${path} must be a full Git object ID.`);
933
- }
934
- }
935
- function requireDigest(value, path) {
936
- if (typeof value !== 'string' || !/^sha256:[a-f0-9]{64}$/u.test(value)) {
937
- throw new Error(`${path} must be a SHA-256 digest.`);
938
- }
939
- }
940
- function requireStringArray(value, path) {
941
- if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string' || entry.trim().length === 0)
942
- || new Set(value).size !== value.length) {
943
- throw new Error(`${path} must contain unique non-empty strings.`);
944
- }
945
- }
946
- function assertPublicationPullRequest(value, path) {
947
- const record = requireObject(value, path);
948
- exactFields(record, [
949
- 'number', 'nodeId', 'url', 'state', 'isDraft', 'headRefName', 'baseRefName',
950
- 'title', 'body', 'authorAssociation',
951
- ], path);
952
- requireInteger(record.number, `${path}.number`, 1);
953
- for (const field of ['nodeId', 'url', 'headRefName', 'baseRefName', 'title', 'body', 'authorAssociation']) {
954
- requireString(record[field], `${path}.${field}`);
955
- }
956
- if (record.state !== 'OPEN' && record.state !== 'CLOSED' && record.state !== 'MERGED') {
957
- throw new Error(`${path}.state is invalid.`);
958
- }
959
- if (typeof record.isDraft !== 'boolean')
960
- throw new Error(`${path}.isDraft must be boolean.`);
961
- }
962
- function assertMutationAttempt(value, path) {
963
- const record = requireObject(value, path);
964
- const allowed = new Set(['kind', 'actionKey', 'stage', 'observationAttempts', 'labels']);
965
- if (Object.keys(record).some((key) => !allowed.has(key)))
966
- throw new Error(`${path} has unexpected fields.`);
967
- if (typeof record.kind !== 'string'
968
- || !['push', 'create-pr', 'add-labels', 'remove-labels', 'post-comment'].includes(record.kind)) {
969
- throw new Error(`${path}.kind is invalid.`);
970
- }
971
- requireString(record.actionKey, `${path}.actionKey`);
972
- if (record.stage !== 'prepared' && record.stage !== 'dispatched') {
973
- throw new Error(`${path}.stage is invalid.`);
974
- }
975
- requireInteger(record.observationAttempts, `${path}.observationAttempts`, 0);
976
- if (record.labels !== undefined)
977
- requireStringArray(record.labels, `${path}.labels`);
978
- const labelKind = record.kind === 'add-labels' || record.kind === 'remove-labels';
979
- if (labelKind !== Array.isArray(record.labels))
980
- throw new Error(`${path}.labels must match its mutation kind.`);
981
- }
982
- function publicationFrom(snapshot, publicationId) {
983
- const stored = snapshot.publications[publicationId];
984
- if (!stored)
985
- throw new Error(`Publication ${publicationId} does not exist.`);
986
- const record = structuredClone(stored.value);
987
- if (record.id !== publicationId || record.revision !== stored.revision) {
988
- throw new Error(`Publication ${publicationId} identity does not match its stored aggregate.`);
989
- }
990
- return record;
991
- }
992
- function publicationOwnerEvent(state) {
993
- if (state === 'review-ready')
994
- return 'publication-review-ready';
995
- if (state === 'external-input-required')
996
- return 'publication-external';
997
- if (state === 'safety-stop')
998
- return 'publication-safety';
999
- return 'publication-cancelled';
1000
- }
1001
- //# sourceMappingURL=mission-publication.js.map