codex-orchestrator 0.1.51 → 2.0.0

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 +21 -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,916 +0,0 @@
1
- import { createHash, randomUUID } from 'node:crypto';
2
- import { constants } from 'node:fs';
3
- import { link, lstat, mkdir, open, readFile, readdir, realpath, rename, unlink, } from 'node:fs/promises';
4
- import { dirname, isAbsolute, join, normalize, relative, resolve, sep } from 'node:path';
5
- import { hostname } from 'node:os';
6
- import { acquireMissionCoordinatorLock } from './mission-coordinator-lock.js';
7
- import { authorizeMissionCapability } from './mission-capability-kernel.js';
8
- import { assertMissionApplyIntent, assertMissionApplyReceipt, missionApplyPermitFingerprint, validateMissionApplyPermit, } from './mission-git-contracts.js';
9
- import { missionStates, safeResumeTargets, terminalMissionStates, } from './mission-state-machine.js';
10
- import { assertPlanParentRecord, planParentScheduleKey, } from './mission-plan-parent.js';
11
- import { assertMissionPublicationRecord, hasScheduledPublicationRecovery, publicationScheduleKey, } from './mission-publication.js';
12
- const mutationTails = new Map();
13
- const processBootNonce = randomUUID();
14
- export class MissionStateStore {
15
- targetRoot;
16
- stateDir;
17
- maxStateBytes;
18
- atomicOperations;
19
- constructor(targetRoot, stateDir, options = {}) {
20
- this.targetRoot = targetRoot;
21
- this.stateDir = stateDir;
22
- const normalizedStateDir = normalize(stateDir);
23
- if (stateDir.length === 0 || isAbsolute(stateDir) || normalizedStateDir === '..'
24
- || normalizedStateDir.startsWith(`..${sep}`)) {
25
- throw new Error('Mission stateDir must stay inside the target root.');
26
- }
27
- this.maxStateBytes = options.maxStateBytes ?? 16 * 1024 * 1024;
28
- this.atomicOperations = {
29
- syncFile: options.atomicOperations?.syncFile ?? ((file) => file.sync()),
30
- rename: options.atomicOperations?.rename ?? ((source, destination) => rename(source, destination)),
31
- syncDirectory: options.atomicOperations?.syncDirectory ?? ((directory) => directory.sync()),
32
- };
33
- if (!Number.isInteger(this.maxStateBytes) || this.maxStateBytes <= 0) {
34
- throw new Error('Mission state maxStateBytes must be a positive integer.');
35
- }
36
- }
37
- statePath() {
38
- return join(this.targetRoot, this.stateDir, 'mission-state-v1.json');
39
- }
40
- stateDirectory() {
41
- return dirname(this.statePath());
42
- }
43
- async temporaryDirectory(name) {
44
- if (!/^[a-z0-9-]+$/u.test(name))
45
- throw new Error('Mission temporary directory name is invalid.');
46
- const directory = join(this.stateDirectory(), name);
47
- await this.ensureDurableDirectory(directory, 0);
48
- return directory;
49
- }
50
- async load() {
51
- await this.assertExistingStatePathSafe();
52
- try {
53
- const content = await readFile(this.statePath(), 'utf8');
54
- const parsed = JSON.parse(content);
55
- assertMissionStateSnapshot(parsed);
56
- const expectedChecksum = checksumFor(parsed);
57
- if (parsed.checksum !== expectedChecksum) {
58
- throw new Error(`Invalid Mission state checksum: expected ${expectedChecksum}, received ${parsed.checksum}`);
59
- }
60
- return parsed;
61
- }
62
- catch (error) {
63
- if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
64
- return createSnapshot(0, emptyDraft());
65
- }
66
- throw error;
67
- }
68
- }
69
- async mutate(expectedGeneration, reducer) {
70
- return this.exclusive((session) => session.mutate(expectedGeneration, reducer));
71
- }
72
- async exclusive(operation) {
73
- return withMutationLock(this.statePath(), async () => {
74
- const coordinator = await this.acquireCoordinator();
75
- try {
76
- return await operation({
77
- load: () => this.load(),
78
- mutate: (expectedGeneration, reducer) => this.commitMutation(expectedGeneration, reducer),
79
- });
80
- }
81
- finally {
82
- await coordinator.release();
83
- }
84
- });
85
- }
86
- async mutateWithBlobs(expectedGeneration, contents, reducer) {
87
- return withMutationLock(this.statePath(), async () => {
88
- const coordinator = await this.acquireCoordinator();
89
- try {
90
- const references = [];
91
- for (const content of contents) {
92
- references.push(await this.putBlob(content, expectedGeneration + 1));
93
- }
94
- return await this.commitMutation(expectedGeneration, (draft) => reducer(draft, references));
95
- }
96
- finally {
97
- await coordinator.release();
98
- }
99
- });
100
- }
101
- async putBlob(content, generation) {
102
- const bytes = Buffer.from(content);
103
- const reference = {
104
- sha256: createHash('sha256').update(bytes).digest('hex'),
105
- size: bytes.byteLength,
106
- };
107
- const directory = join(this.targetRoot, this.stateDir, 'mission-blobs');
108
- const path = join(directory, reference.sha256);
109
- await this.ensureDurableDirectory(directory, generation);
110
- const tempPath = join(directory, `.${reference.sha256}.${process.pid}.${randomUUID()}.tmp`);
111
- const file = await open(tempPath, 'wx', 0o600);
112
- try {
113
- await file.writeFile(bytes);
114
- await file.sync();
115
- }
116
- finally {
117
- await file.close();
118
- }
119
- try {
120
- await link(tempPath, path);
121
- }
122
- catch (error) {
123
- if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST')) {
124
- throw error;
125
- }
126
- }
127
- finally {
128
- await unlink(tempPath).catch(() => undefined);
129
- }
130
- const directoryHandle = await open(directory, 'r');
131
- try {
132
- await directoryHandle.sync();
133
- }
134
- finally {
135
- await directoryHandle.close();
136
- }
137
- await this.readBlob(reference);
138
- return reference;
139
- }
140
- async ensureDurableDirectory(directory, generation) {
141
- const root = resolve(this.targetRoot);
142
- const resolvedDirectory = resolve(directory);
143
- if (resolvedDirectory !== root && !resolvedDirectory.startsWith(`${root}${sep}`)) {
144
- throw new Error('Mission state directory is outside the target root.');
145
- }
146
- await this.ensureNoFollowDirectories(root, resolvedDirectory, true);
147
- for (let current = resolvedDirectory === root ? root : dirname(resolvedDirectory);; current = dirname(current)) {
148
- const handle = await open(current, 'r');
149
- try {
150
- await this.atomicOperations.syncDirectory(handle, generation);
151
- }
152
- finally {
153
- await handle.close();
154
- }
155
- if (current === root)
156
- break;
157
- if (current === dirname(current)) {
158
- throw new Error('Mission state directory is outside the target root.');
159
- }
160
- }
161
- }
162
- async ensureNoFollowDirectories(root, directory, create) {
163
- const rootStat = await lstat(root);
164
- if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
165
- throw new Error('Mission target root must be a directory.');
166
- }
167
- const canonicalRoot = await realpath(root);
168
- let current = root;
169
- for (const segment of relative(root, directory).split(sep).filter(Boolean)) {
170
- current = join(current, segment);
171
- try {
172
- const existing = await lstat(current);
173
- if (!existing.isDirectory() || existing.isSymbolicLink()) {
174
- throw new Error(`Mission state path component is not a direct directory: ${current}.`);
175
- }
176
- }
177
- catch (error) {
178
- if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT'))
179
- throw error;
180
- if (!create)
181
- return false;
182
- try {
183
- await mkdir(current, { mode: 0o700 });
184
- }
185
- catch (mkdirError) {
186
- if (!(mkdirError instanceof Error && 'code' in mkdirError && mkdirError.code === 'EEXIST')) {
187
- throw mkdirError;
188
- }
189
- }
190
- const created = await lstat(current);
191
- if (!created.isDirectory() || created.isSymbolicLink()) {
192
- throw new Error(`Mission state path component is not a direct directory: ${current}.`);
193
- }
194
- }
195
- }
196
- const expectedCanonicalDirectory = join(canonicalRoot, relative(root, directory));
197
- if (await realpath(directory) !== expectedCanonicalDirectory) {
198
- throw new Error('Mission state directory resolves through a symlink.');
199
- }
200
- return true;
201
- }
202
- async assertExistingStatePathSafe() {
203
- const root = resolve(this.targetRoot);
204
- const directory = this.stateDirectory();
205
- await this.ensureNoFollowDirectories(root, directory, false);
206
- }
207
- async readBlob(reference) {
208
- if (!/^[a-f0-9]{64}$/u.test(reference.sha256)
209
- || !Number.isSafeInteger(reference.size) || reference.size < 0) {
210
- throw new Error('Invalid Mission blob reference.');
211
- }
212
- await this.assertExistingStatePathSafe();
213
- await this.ensureNoFollowDirectories(resolve(this.targetRoot), join(this.stateDirectory(), 'mission-blobs'), false);
214
- const path = join(this.targetRoot, this.stateDir, 'mission-blobs', reference.sha256);
215
- const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
216
- let content;
217
- try {
218
- const before = await file.stat({ bigint: true });
219
- if (!before.isFile() || before.nlink !== 1n || before.size !== BigInt(reference.size)) {
220
- throw new Error(`Invalid Mission blob ${reference.sha256}: unsafe file identity.`);
221
- }
222
- content = await file.readFile();
223
- const after = await file.stat({ bigint: true });
224
- if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size
225
- || before.mtimeNs !== after.mtimeNs || after.nlink !== 1n) {
226
- throw new Error(`Invalid Mission blob ${reference.sha256}: file changed during read.`);
227
- }
228
- }
229
- finally {
230
- await file.close();
231
- }
232
- const actualHash = createHash('sha256').update(content).digest('hex');
233
- if (content.byteLength !== reference.size || actualHash !== reference.sha256) {
234
- throw new Error(`Invalid Mission blob ${reference.sha256}: content does not match reference.`);
235
- }
236
- return content;
237
- }
238
- async pruneUnreferencedBlobs() {
239
- return withMutationLock(this.statePath(), async () => {
240
- const coordinator = await this.acquireCoordinator();
241
- try {
242
- const snapshot = await this.load();
243
- const directory = join(this.targetRoot, this.stateDir, 'mission-blobs');
244
- let entries;
245
- try {
246
- entries = await readdir(directory, { withFileTypes: true });
247
- }
248
- catch (error) {
249
- if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
250
- return [];
251
- }
252
- throw error;
253
- }
254
- const removed = [];
255
- for (const entry of entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)) {
256
- if (!entry.isFile() || !/^[a-f0-9]{64}$/u.test(entry.name) || snapshot.blobs[entry.name]) {
257
- continue;
258
- }
259
- await unlink(join(directory, entry.name));
260
- removed.push(entry.name);
261
- }
262
- if (removed.length > 0) {
263
- const directoryHandle = await open(directory, 'r');
264
- try {
265
- await directoryHandle.sync();
266
- }
267
- finally {
268
- await directoryHandle.close();
269
- }
270
- }
271
- return removed;
272
- }
273
- finally {
274
- await coordinator.release();
275
- }
276
- });
277
- }
278
- async acquireCoordinator() {
279
- await this.ensureDurableDirectory(this.stateDirectory(), 0);
280
- return acquireMissionCoordinatorLock({
281
- targetRoot: this.targetRoot,
282
- stateDir: this.stateDir,
283
- hostId: hostname(),
284
- bootNonce: processBootNonce,
285
- waitTimeoutMs: 5_000,
286
- });
287
- }
288
- async commitMutation(expectedGeneration, reducer) {
289
- const current = await this.load();
290
- if (current.generation !== expectedGeneration) {
291
- throw new Error(`Mission state generation conflict: expected ${expectedGeneration}, current ${current.generation}`);
292
- }
293
- const draft = cloneDraft(current);
294
- reducer(draft);
295
- const next = createSnapshot(current.generation + 1, draft);
296
- assertMissionStateSnapshot(next);
297
- for (const reference of Object.values(next.blobs)) {
298
- await this.readBlob(reference);
299
- }
300
- await this.atomicWrite(next);
301
- return next;
302
- }
303
- async atomicWrite(snapshot) {
304
- const path = this.statePath();
305
- const directory = dirname(path);
306
- const serialized = `${canonicalJson(snapshot)}\n`;
307
- const size = Buffer.byteLength(serialized, 'utf8');
308
- if (size > this.maxStateBytes) {
309
- throw new Error(`Mission state size limit exceeded: ${size} > ${this.maxStateBytes} bytes.`);
310
- }
311
- await this.ensureDurableDirectory(directory, snapshot.generation);
312
- const tempPath = join(directory, `.mission-state-v1.${process.pid}.${randomUUID()}.tmp`);
313
- let tempCreated = false;
314
- try {
315
- const file = await open(tempPath, 'wx', 0o600);
316
- tempCreated = true;
317
- try {
318
- await file.writeFile(serialized, 'utf8');
319
- await this.atomicOperations.syncFile(file, snapshot.generation);
320
- }
321
- finally {
322
- await file.close();
323
- }
324
- await this.atomicOperations.rename(tempPath, path, snapshot.generation);
325
- tempCreated = false;
326
- const directoryHandle = await open(directory, 'r');
327
- try {
328
- await this.atomicOperations.syncDirectory(directoryHandle, snapshot.generation);
329
- }
330
- finally {
331
- await directoryHandle.close();
332
- }
333
- }
334
- finally {
335
- if (tempCreated) {
336
- await unlink(tempPath).catch(() => undefined);
337
- }
338
- }
339
- }
340
- }
341
- async function withMutationLock(key, operation) {
342
- const previous = mutationTails.get(key) ?? Promise.resolve();
343
- let release;
344
- const gate = new Promise((resolve) => {
345
- release = resolve;
346
- });
347
- const tail = previous.catch(() => undefined).then(() => gate);
348
- mutationTails.set(key, tail);
349
- await previous.catch(() => undefined);
350
- try {
351
- return await operation();
352
- }
353
- finally {
354
- release();
355
- void tail.finally(() => {
356
- if (mutationTails.get(key) === tail) {
357
- mutationTails.delete(key);
358
- }
359
- });
360
- }
361
- }
362
- function emptyDraft() {
363
- return {
364
- missions: {},
365
- planParents: {},
366
- publications: {},
367
- reservations: {},
368
- nextEligibleAt: {},
369
- tombstones: {},
370
- blobs: {},
371
- };
372
- }
373
- function cloneDraft(snapshot) {
374
- return structuredClone({
375
- missions: snapshot.missions,
376
- planParents: snapshot.planParents,
377
- publications: snapshot.publications,
378
- reservations: snapshot.reservations,
379
- nextEligibleAt: snapshot.nextEligibleAt,
380
- tombstones: snapshot.tombstones,
381
- blobs: snapshot.blobs,
382
- });
383
- }
384
- function createSnapshot(generation, draft) {
385
- const withoutChecksum = {
386
- version: 1,
387
- generation,
388
- ...draft,
389
- };
390
- return {
391
- ...withoutChecksum,
392
- checksum: checksumFor(withoutChecksum),
393
- };
394
- }
395
- function checksumFor(snapshot) {
396
- const { checksum: _checksum, ...content } = snapshot;
397
- return `sha256:${createHash('sha256').update(Buffer.from(canonicalJson(content), 'utf8')).digest('hex')}`;
398
- }
399
- function canonicalJson(value) {
400
- return JSON.stringify(canonicalValue(value));
401
- }
402
- function canonicalValue(value) {
403
- if (Array.isArray(value)) {
404
- return value.map(canonicalValue);
405
- }
406
- if (typeof value === 'object' && value !== null) {
407
- return Object.fromEntries(Object.entries(value)
408
- .filter(([, child]) => child !== undefined)
409
- .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
410
- .map(([key, child]) => [key, canonicalValue(child)]));
411
- }
412
- return value;
413
- }
414
- function assertMissionStateSnapshot(value) {
415
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
416
- throw new Error('Invalid Mission state: root must be an object.');
417
- }
418
- const record = value;
419
- const allowedRootFields = new Set([
420
- 'version',
421
- 'generation',
422
- 'checksum',
423
- 'missions',
424
- 'planParents',
425
- 'publications',
426
- 'reservations',
427
- 'nextEligibleAt',
428
- 'tombstones',
429
- 'blobs',
430
- ]);
431
- for (const key of Object.keys(record)) {
432
- if (!allowedRootFields.has(key)) {
433
- throw new Error(`Invalid Mission state: unexpected field ${key}.`);
434
- }
435
- }
436
- if (record.version !== 1) {
437
- throw new Error('Invalid Mission state: version must be 1.');
438
- }
439
- if (!Number.isInteger(record.generation) || record.generation < 0) {
440
- throw new Error('Invalid Mission state: generation must be a non-negative integer.');
441
- }
442
- if (typeof record.checksum !== 'string' || !/^sha256:[a-f0-9]{64}$/u.test(record.checksum)) {
443
- throw new Error('Invalid Mission state: checksum must be sha256 hex.');
444
- }
445
- for (const key of [
446
- 'missions',
447
- 'planParents',
448
- 'publications',
449
- 'reservations',
450
- 'nextEligibleAt',
451
- 'tombstones',
452
- 'blobs',
453
- ]) {
454
- const child = record[key];
455
- if (typeof child !== 'object' || child === null || Array.isArray(child)) {
456
- throw new Error(`Invalid Mission state: ${key} must be an object.`);
457
- }
458
- }
459
- for (const [id, mission] of Object.entries(record.missions)) {
460
- assertMissionRecord(mission, `missions.${id}`);
461
- if (mission.id !== id) {
462
- invalid(`missions.${id}.id must equal its map key`);
463
- }
464
- if (mission.authorizedPermit && (mission.authorizedPermit.missionId !== id
465
- || mission.authorizedPermit.actionKey !== mission.actionKey
466
- || mission.authorizedPermit.inputSnapshot !== mission.inputSnapshot
467
- || mission.authorizedPermit.fencingEpoch !== mission.fencingEpoch)) {
468
- invalid(`missions.${id}.authorizedPermit must match the aggregate identity`);
469
- }
470
- }
471
- for (const [id, parent] of Object.entries(record.planParents)) {
472
- assertPlanParentRecord(parent, `planParents.${id}`);
473
- if (parent.id !== id)
474
- invalid(`planParents.${id}.id must equal its map key`);
475
- }
476
- for (const [id, aggregate] of Object.entries(record.publications)) {
477
- assertStoredAggregate(aggregate, `publications.${id}`);
478
- assertMissionPublicationRecord(aggregate.value, `publications.${id}.value`);
479
- const publication = aggregate.value;
480
- if (publication.id !== id)
481
- invalid(`publications.${id}.value.id must equal its map key`);
482
- if (publication.revision !== aggregate.revision)
483
- invalid(`publications.${id}.revision must match value.revision`);
484
- const missionOwner = Boolean(record.missions[publication.ownerId]);
485
- const parentOwner = Boolean(record.planParents[publication.ownerId]);
486
- if (!missionOwner && !parentOwner) {
487
- invalid(`publications.${id}.value.ownerId references a missing owner`);
488
- }
489
- if (missionOwner && parentOwner)
490
- invalid(`publications.${id}.value.ownerId is ambiguous`);
491
- const ownerMission = record.missions[publication.ownerId];
492
- const ownerParent = record.planParents[publication.ownerId];
493
- if (publication.state === 'resumable') {
494
- const missionMapped = ownerMission?.state === 'resumable'
495
- && ownerMission.resumeTarget === 'publication-prepared'
496
- && ownerMission.nextEligibleAt === publication.nextEligibleAt
497
- && ownerMission.actionKey === publication.actionKey;
498
- const parentMapped = ownerParent?.state === 'wave-waiting'
499
- && ownerParent.resumeTarget === 'publication-prepared'
500
- && ownerParent.nextEligibleAt === publication.nextEligibleAt
501
- && ownerParent.actionKey === publication.actionKey;
502
- if (!missionMapped && !parentMapped)
503
- invalid(`publications.${id} resumable state must match its owner`);
504
- }
505
- }
506
- for (const [id, aggregate] of Object.entries(record.reservations)) {
507
- assertStoredAggregate(aggregate, `reservations.${id}`);
508
- }
509
- for (const [id, nextEligibleAt] of Object.entries(record.nextEligibleAt)) {
510
- assertNonEmptyString(nextEligibleAt, `nextEligibleAt.${id}`);
511
- if (id.startsWith('publication:')) {
512
- const publicationId = id.slice('publication:'.length);
513
- const aggregate = record.publications[publicationId];
514
- const publication = aggregate?.value;
515
- if (!publication || (publication.state !== 'resumable' && !hasScheduledPublicationRecovery(publication))
516
- || publication.nextEligibleAt !== nextEligibleAt
517
- || publicationScheduleKey(publication.id) !== id) {
518
- invalid(`nextEligibleAt.${id} does not match a resumable Publication`);
519
- }
520
- continue;
521
- }
522
- if (id.startsWith('plan-parent:')) {
523
- const parentId = id.slice('plan-parent:'.length);
524
- const parent = record.planParents[parentId];
525
- const waiting = parent?.state === 'wave-waiting' && parent.nextEligibleAt === nextEligibleAt;
526
- const claimed = parent?.claim?.leaseUntil === nextEligibleAt;
527
- if (!parent || (!waiting && !claimed) || planParentScheduleKey(parent.id) !== id) {
528
- invalid(`nextEligibleAt.${id} does not match a waiting Plan Parent`);
529
- }
530
- continue;
531
- }
532
- const mission = record.missions[id];
533
- if (!mission)
534
- invalid(`nextEligibleAt.${id} references a missing mission`);
535
- if (mission.state === 'resumable') {
536
- if (mission.nextEligibleAt !== nextEligibleAt || mission.claim !== undefined) {
537
- invalid(`nextEligibleAt.${id} does not match resumable mission state`);
538
- }
539
- }
540
- else if (!mission.claim || mission.claim.leaseUntil !== nextEligibleAt) {
541
- invalid(`nextEligibleAt.${id} does not match an active claim lease`);
542
- }
543
- }
544
- for (const [id, tombstone] of Object.entries(record.tombstones)) {
545
- assertTombstone(tombstone, `tombstones.${id}`);
546
- }
547
- for (const [id, aggregate] of Object.entries(record.publications)) {
548
- const publication = aggregate.value;
549
- const indexed = record.nextEligibleAt[publicationScheduleKey(id)];
550
- if ((publication.state === 'resumable' || hasScheduledPublicationRecovery(publication))
551
- && indexed !== publication.nextEligibleAt) {
552
- invalid(`publications.${id} scheduled recovery requires matching nextEligibleAt index`);
553
- }
554
- if (publication.state !== 'resumable' && !hasScheduledPublicationRecovery(publication)
555
- && indexed !== undefined) {
556
- invalid(`publications.${id} has stale scheduling metadata`);
557
- }
558
- }
559
- for (const [hash, blob] of Object.entries(record.blobs)) {
560
- assertBlobReference(blob, `blobs.${hash}`, hash);
561
- }
562
- for (const [id, mission] of Object.entries(record.missions)) {
563
- const linkedPublication = Object.values(record.publications)
564
- .map((aggregate) => aggregate.value)
565
- .find((publication) => publication.ownerId === id && publication.state === 'resumable');
566
- if (mission.state === 'resumable') {
567
- if (!mission.resumeTarget || !mission.nextEligibleAt || !mission.resumableReason
568
- || !mission.requiredPredicate
569
- || (record.nextEligibleAt[id] !== mission.nextEligibleAt
570
- && linkedPublication?.nextEligibleAt !== mission.nextEligibleAt)) {
571
- invalid(`missions.${id} resumable state requires complete indexed scheduling metadata`);
572
- }
573
- }
574
- if (mission.claim
575
- && record.nextEligibleAt[id] !== mission.claim.leaseUntil) {
576
- invalid(`missions.${id}.claim requires matching nextEligibleAt index`);
577
- }
578
- if (mission.claim && terminalMissionState(mission.state)) {
579
- invalid(`missions.${id}.claim is forbidden in terminal state`);
580
- }
581
- for (const [actionKey, execution] of Object.entries(mission.actionExecutions ?? {})) {
582
- if (execution.status === 'completed'
583
- && !record.blobs[execution.receiptSha256]) {
584
- invalid(`missions.${id}.actionExecutions.${actionKey} references a missing receipt blob`);
585
- }
586
- }
587
- }
588
- for (const [id, parent] of Object.entries(record.planParents)) {
589
- const scheduleKey = planParentScheduleKey(id);
590
- const linkedPublication = Object.values(record.publications)
591
- .map((aggregate) => aggregate.value)
592
- .find((publication) => publication.ownerId === id && publication.state === 'resumable');
593
- if (parent.state === 'wave-waiting') {
594
- if (record.nextEligibleAt[scheduleKey] !== parent.nextEligibleAt
595
- && linkedPublication?.nextEligibleAt !== parent.nextEligibleAt) {
596
- invalid(`planParents.${id} requires matching nextEligibleAt index`);
597
- }
598
- }
599
- else if (record.nextEligibleAt[scheduleKey] !== undefined
600
- && (!parent.claim || parent.claim.leaseUntil
601
- !== record.nextEligibleAt[scheduleKey])) {
602
- invalid(`planParents.${id} has stale scheduling metadata`);
603
- }
604
- }
605
- }
606
- const missionStateSet = new Set(missionStates);
607
- const resumeTargetSet = new Set(safeResumeTargets);
608
- function assertMissionRecord(value, path) {
609
- const record = assertObject(value, path);
610
- assertExactFields(record, path, [
611
- 'id',
612
- 'revision',
613
- 'state',
614
- 'findingIds',
615
- 'residualFindingIds',
616
- 'resumeTarget',
617
- 'nextEligibleAt',
618
- 'resumableReason',
619
- 'requiredPredicate',
620
- 'actionKey',
621
- 'inputSnapshot',
622
- 'fencingEpoch',
623
- 'authorizedPermit',
624
- 'actionExecutions',
625
- 'applyPermit',
626
- 'applyIntent',
627
- 'applyReceipt',
628
- 'applyHistory',
629
- 'claim',
630
- 'cancellation',
631
- ]);
632
- assertNonEmptyString(record.id, `${path}.id`);
633
- assertNonNegativeInteger(record.revision, `${path}.revision`);
634
- if (typeof record.state !== 'string' || !missionStateSet.has(record.state)) {
635
- invalid(`${path}.state must be a known Mission state`);
636
- }
637
- assertOptionalStringArray(record.findingIds, `${path}.findingIds`);
638
- assertOptionalStringArray(record.residualFindingIds, `${path}.residualFindingIds`);
639
- if (record.resumeTarget !== undefined
640
- && (typeof record.resumeTarget !== 'string' || !resumeTargetSet.has(record.resumeTarget))) {
641
- invalid(`${path}.resumeTarget must be a safe resume target`);
642
- }
643
- assertOptionalNonEmptyString(record.nextEligibleAt, `${path}.nextEligibleAt`);
644
- assertOptionalNonEmptyString(record.resumableReason, `${path}.resumableReason`);
645
- assertOptionalNonEmptyString(record.requiredPredicate, `${path}.requiredPredicate`);
646
- if (record.nextEligibleAt !== undefined) {
647
- assertExactTimestamp(record.nextEligibleAt, `${path}.nextEligibleAt`);
648
- }
649
- assertOptionalNonEmptyString(record.actionKey, `${path}.actionKey`);
650
- assertOptionalNonEmptyString(record.inputSnapshot, `${path}.inputSnapshot`);
651
- if (record.fencingEpoch !== undefined) {
652
- assertPositiveInteger(record.fencingEpoch, `${path}.fencingEpoch`);
653
- }
654
- if (record.authorizedPermit !== undefined) {
655
- assertAuthorizedPermit(record.authorizedPermit, `${path}.authorizedPermit`);
656
- }
657
- if (record.actionExecutions !== undefined) {
658
- const executions = assertObject(record.actionExecutions, `${path}.actionExecutions`);
659
- for (const [actionKey, execution] of Object.entries(executions)) {
660
- assertNonEmptyString(actionKey, `${path}.actionExecutions key`);
661
- const entry = assertObject(execution, `${path}.actionExecutions.${actionKey}`);
662
- assertExactFields(entry, `${path}.actionExecutions.${actionKey}`, [
663
- 'permitFingerprint', 'status', 'receiptSha256',
664
- ]);
665
- if (typeof entry.permitFingerprint !== 'string'
666
- || !/^sha256:[a-f0-9]{64}$/u.test(entry.permitFingerprint)) {
667
- invalid(`${path}.actionExecutions.${actionKey}.permitFingerprint must be sha256 hex`);
668
- }
669
- if (entry.status !== 'in-flight' && entry.status !== 'completed') {
670
- invalid(`${path}.actionExecutions.${actionKey}.status must be in-flight or completed`);
671
- }
672
- assertOptionalNonEmptyString(entry.receiptSha256, `${path}.actionExecutions.${actionKey}.receiptSha256`);
673
- if (entry.status === 'completed'
674
- && (typeof entry.receiptSha256 !== 'string' || !/^[a-f0-9]{64}$/u.test(entry.receiptSha256))) {
675
- invalid(`${path}.actionExecutions.${actionKey}.receiptSha256 must be sha256 hex when completed`);
676
- }
677
- if (entry.status === 'in-flight' && entry.receiptSha256 !== undefined) {
678
- invalid(`${path}.actionExecutions.${actionKey}.receiptSha256 is forbidden while in flight`);
679
- }
680
- }
681
- }
682
- if (record.applyPermit !== undefined) {
683
- const permit = validateMissionApplyPermit(record.applyPermit);
684
- if (permit.missionId !== record.id || permit.actionKey !== record.actionKey
685
- || permit.fencingEpoch !== record.fencingEpoch) {
686
- invalid(`${path}.applyPermit must match the aggregate identity`);
687
- }
688
- }
689
- if (record.applyIntent !== undefined) {
690
- assertMissionApplyIntent(record.applyIntent);
691
- if (record.applyPermit === undefined
692
- || record.applyIntent.permitFingerprint
693
- !== missionApplyPermitFingerprint(validateMissionApplyPermit(record.applyPermit))) {
694
- invalid(`${path}.applyIntent must match applyPermit`);
695
- }
696
- }
697
- if (record.applyReceipt !== undefined) {
698
- assertMissionApplyReceipt(record.applyReceipt);
699
- if (record.applyIntent === undefined
700
- || record.applyReceipt.permitFingerprint !== record.applyIntent.permitFingerprint) {
701
- invalid(`${path}.applyReceipt must match applyIntent`);
702
- }
703
- const permit = record.applyPermit === undefined
704
- ? undefined
705
- : validateMissionApplyPermit(record.applyPermit);
706
- if (!permit
707
- || record.applyReceipt.targetRef !== permit.targetRef
708
- || record.applyReceipt.oldCommitSha !== permit.expectedOldCommit
709
- || record.applyReceipt.commitSha !== permit.expectedNewCommit
710
- || record.applyReceipt.treeSha !== permit.expectedNewTree) {
711
- invalid(`${path}.applyReceipt identity must match applyPermit`);
712
- }
713
- }
714
- if (record.applyHistory !== undefined) {
715
- if (!Array.isArray(record.applyHistory)) {
716
- invalid(`${path}.applyHistory must be an array`);
717
- }
718
- for (const [index, receipt] of record.applyHistory.entries()) {
719
- assertMissionApplyReceipt(receipt);
720
- if (record.applyHistory.findIndex((candidate) => candidate.permitFingerprint === receipt.permitFingerprint) !== index) {
721
- invalid(`${path}.applyHistory contains duplicate permit fingerprints`);
722
- }
723
- }
724
- }
725
- if (record.claim !== undefined) {
726
- assertMissionClaim(record.claim, `${path}.claim`);
727
- }
728
- if (record.cancellation !== undefined) {
729
- const cancellation = assertObject(record.cancellation, `${path}.cancellation`);
730
- assertExactFields(cancellation, `${path}.cancellation`, ['requestedAt', 'requestedBy']);
731
- assertExactTimestamp(cancellation.requestedAt, `${path}.cancellation.requestedAt`);
732
- assertNonEmptyString(cancellation.requestedBy, `${path}.cancellation.requestedBy`);
733
- if (record.state !== 'cancelling' && record.state !== 'cancelled'
734
- && record.state !== 'safety-stop') {
735
- invalid(`${path}.cancellation is allowed only while cancelling, cancelled, or safety-stop`);
736
- }
737
- }
738
- const hasPermit = record.applyPermit !== undefined;
739
- const hasIntent = record.applyIntent !== undefined;
740
- const hasReceipt = record.applyReceipt !== undefined;
741
- if (record.state === 'apply-authorizing' && (hasPermit || hasIntent || hasReceipt)) {
742
- invalid(`${path} apply-authorizing state must not retain apply artifacts`);
743
- }
744
- if (record.state === 'apply-prepared' && (!hasPermit || hasIntent || hasReceipt)) {
745
- invalid(`${path} apply-prepared state requires only an apply permit`);
746
- }
747
- if (record.state === 'applying' && (!hasPermit || !hasIntent || hasReceipt)) {
748
- invalid(`${path} applying state requires permit and intent without receipt`);
749
- }
750
- if (record.state === 'reconciling' && (hasPermit || hasIntent || hasReceipt)
751
- && (!hasPermit || !hasIntent || !hasReceipt)) {
752
- invalid(`${path} apply reconciliation requires permit, intent, and receipt together`);
753
- }
754
- }
755
- function assertMissionClaim(value, path) {
756
- const record = assertObject(value, path);
757
- assertExactFields(record, path, [
758
- 'version', 'token', 'daemonId', 'hostId', 'bootNonce', 'fencingEpoch',
759
- 'claimedAt', 'leaseUntil', 'processes',
760
- ]);
761
- if (record.version !== 1)
762
- invalid(`${path}.version must be 1`);
763
- assertNonEmptyString(record.token, `${path}.token`);
764
- assertNonEmptyString(record.daemonId, `${path}.daemonId`);
765
- assertNonEmptyString(record.hostId, `${path}.hostId`);
766
- assertNonEmptyString(record.bootNonce, `${path}.bootNonce`);
767
- assertPositiveInteger(record.fencingEpoch, `${path}.fencingEpoch`);
768
- assertExactTimestamp(record.claimedAt, `${path}.claimedAt`);
769
- assertExactTimestamp(record.leaseUntil, `${path}.leaseUntil`);
770
- if (record.leaseUntil <= record.claimedAt) {
771
- invalid(`${path}.leaseUntil must be after claimedAt`);
772
- }
773
- if (!Array.isArray(record.processes))
774
- invalid(`${path}.processes must be an array`);
775
- const identities = new Set();
776
- record.processes.forEach((value, index) => {
777
- const process = assertObject(value, `${path}.processes[${index}]`);
778
- assertExactFields(process, `${path}.processes[${index}]`, [
779
- 'actionKey', 'pid', 'hostId', 'bootNonce', 'startedAt',
780
- ]);
781
- assertNonEmptyString(process.actionKey, `${path}.processes[${index}].actionKey`);
782
- assertPositiveInteger(process.pid, `${path}.processes[${index}].pid`);
783
- assertNonEmptyString(process.hostId, `${path}.processes[${index}].hostId`);
784
- assertNonEmptyString(process.bootNonce, `${path}.processes[${index}].bootNonce`);
785
- assertExactTimestamp(process.startedAt, `${path}.processes[${index}].startedAt`);
786
- if (process.hostId !== record.hostId || process.bootNonce !== record.bootNonce) {
787
- invalid(`${path}.processes[${index}] must match claim host and boot identity`);
788
- }
789
- const identity = `${process.hostId}:${process.bootNonce}:${process.pid}:${process.actionKey}`;
790
- if (identities.has(identity))
791
- invalid(`${path}.processes contains duplicate ${identity}`);
792
- identities.add(identity);
793
- });
794
- }
795
- function assertAuthorizedPermit(value, path) {
796
- const record = assertObject(value, path);
797
- assertExactFields(record, path, [
798
- 'missionId', 'actionKey', 'capability', 'argv', 'requestedPaths', 'grantedPaths',
799
- 'inputSnapshot', 'fencingEpoch', 'expiresAt', 'network', 'workspace',
800
- 'readPath', 'maxReadBytes',
801
- ]);
802
- if (record.network !== 'deny' || record.workspace !== 'read-only') {
803
- invalid(`${path} must deny network and keep the workspace read-only`);
804
- }
805
- try {
806
- authorizeMissionCapability(record);
807
- }
808
- catch (error) {
809
- invalid(`${path} is invalid: ${error instanceof Error ? error.message : String(error)}`);
810
- }
811
- }
812
- function assertStoredAggregate(value, path) {
813
- const record = assertObject(value, path);
814
- assertExactFields(record, path, ['revision', 'value']);
815
- assertNonNegativeInteger(record.revision, `${path}.revision`);
816
- assertJsonValue(record.value, `${path}.value`);
817
- }
818
- function assertTombstone(value, path) {
819
- const record = assertObject(value, path);
820
- assertExactFields(record, path, ['kind', 'terminalState', 'retainedAt']);
821
- if (record.kind !== 'mission' && record.kind !== 'plan-parent' && record.kind !== 'publication') {
822
- invalid(`${path}.kind must be a known aggregate kind`);
823
- }
824
- assertNonEmptyString(record.terminalState, `${path}.terminalState`);
825
- assertNonEmptyString(record.retainedAt, `${path}.retainedAt`);
826
- }
827
- function assertBlobReference(value, path, expectedHash) {
828
- const record = assertObject(value, path);
829
- assertExactFields(record, path, ['sha256', 'size']);
830
- if (typeof record.sha256 !== 'string' || !/^[a-f0-9]{64}$/u.test(record.sha256)) {
831
- invalid(`${path}.sha256 must be lowercase SHA-256 hex`);
832
- }
833
- if (expectedHash !== undefined && record.sha256 !== expectedHash) {
834
- invalid(`${path}.sha256 must match its map key`);
835
- }
836
- assertNonNegativeInteger(record.size, `${path}.size`);
837
- }
838
- function assertJsonValue(value, path) {
839
- if (value === null || typeof value === 'string' || typeof value === 'boolean') {
840
- return;
841
- }
842
- if (typeof value === 'number') {
843
- if (!Number.isFinite(value)) {
844
- invalid(`${path} must contain only finite JSON numbers`);
845
- }
846
- return;
847
- }
848
- if (Array.isArray(value)) {
849
- value.forEach((child, index) => assertJsonValue(child, `${path}[${index}]`));
850
- return;
851
- }
852
- if (typeof value === 'object' && value !== null) {
853
- for (const [key, child] of Object.entries(value)) {
854
- assertJsonValue(child, `${path}.${key}`);
855
- }
856
- return;
857
- }
858
- invalid(`${path} must be valid JSON`);
859
- }
860
- function assertObject(value, path) {
861
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
862
- invalid(`${path} must be an object`);
863
- }
864
- return value;
865
- }
866
- function assertExactFields(record, path, fields) {
867
- const allowed = new Set(fields);
868
- for (const key of Object.keys(record)) {
869
- if (!allowed.has(key)) {
870
- invalid(`${path} has unexpected field ${key}`);
871
- }
872
- }
873
- }
874
- function assertNonNegativeInteger(value, path) {
875
- if (!Number.isSafeInteger(value) || value < 0) {
876
- invalid(`${path} must be a non-negative integer`);
877
- }
878
- }
879
- function assertPositiveInteger(value, path) {
880
- if (!Number.isSafeInteger(value) || value <= 0) {
881
- invalid(`${path} must be a positive integer`);
882
- }
883
- }
884
- function assertNonEmptyString(value, path) {
885
- if (typeof value !== 'string' || value.trim().length === 0) {
886
- invalid(`${path} must be a non-empty string`);
887
- }
888
- }
889
- function assertOptionalNonEmptyString(value, path) {
890
- if (value !== undefined) {
891
- assertNonEmptyString(value, path);
892
- }
893
- }
894
- function assertExactTimestamp(value, path) {
895
- assertNonEmptyString(value, path);
896
- const milliseconds = Date.parse(value);
897
- if (!Number.isFinite(milliseconds) || new Date(milliseconds).toISOString() !== value) {
898
- invalid(`${path} must be an exact UTC ISO timestamp`);
899
- }
900
- }
901
- function terminalMissionState(value) {
902
- return terminalMissionStates.has(value);
903
- }
904
- function assertOptionalStringArray(value, path) {
905
- if (value === undefined) {
906
- return;
907
- }
908
- if (!Array.isArray(value)) {
909
- invalid(`${path} must be an array`);
910
- }
911
- value.forEach((item, index) => assertNonEmptyString(item, `${path}[${index}]`));
912
- }
913
- function invalid(reason) {
914
- throw new Error(`Invalid Mission state: ${reason}.`);
915
- }
916
- //# sourceMappingURL=mission-state-store.js.map