oh-my-super-agent 0.1.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 (461) hide show
  1. package/.agents/agents/advisor.md +15 -0
  2. package/.agents/agents/conductor.md +32 -0
  3. package/.agents/agents/dreamer.md +13 -0
  4. package/.agents/agents/executor.md +17 -0
  5. package/.agents/agents/grader.md +15 -0
  6. package/.agents/config/endurance-policy.json +8 -0
  7. package/.agents/config/oms-config.json +119 -0
  8. package/.agents/config/release-policy.json +12 -0
  9. package/.agents/config/secret-scan-policy.json +7 -0
  10. package/.agents/config/verification-plan.json +33 -0
  11. package/.agents/registry.json +11 -0
  12. package/.agents/rules/constitution.md +7 -0
  13. package/.agents/skills/conductor/SKILL.md +23 -0
  14. package/.agents/skills/dream/SKILL.md +14 -0
  15. package/.agents/skills/executor/SKILL.md +13 -0
  16. package/.agents/skills/oms/SKILL.md +52 -0
  17. package/.agents/skills/quota-aware/SKILL.md +12 -0
  18. package/.agents/skills/schedule/SKILL.md +13 -0
  19. package/.agents/skills/team-protocol/SKILL.md +20 -0
  20. package/LICENSE +21 -0
  21. package/README.md +146 -0
  22. package/dist/src/audit/run-ledger.d.ts +183 -0
  23. package/dist/src/audit/run-ledger.js +1655 -0
  24. package/dist/src/audit/run-ledger.js.map +1 -0
  25. package/dist/src/cli.d.ts +61 -0
  26. package/dist/src/cli.js +2096 -0
  27. package/dist/src/cli.js.map +1 -0
  28. package/dist/src/commands/github-queue-probe.d.ts +26 -0
  29. package/dist/src/commands/github-queue-probe.js +51 -0
  30. package/dist/src/commands/github-queue-probe.js.map +1 -0
  31. package/dist/src/commands/trace-fixture.d.ts +20 -0
  32. package/dist/src/commands/trace-fixture.js +72 -0
  33. package/dist/src/commands/trace-fixture.js.map +1 -0
  34. package/dist/src/conductor/activity.d.ts +46 -0
  35. package/dist/src/conductor/activity.js +224 -0
  36. package/dist/src/conductor/activity.js.map +1 -0
  37. package/dist/src/conductor/bridge.d.ts +53 -0
  38. package/dist/src/conductor/bridge.js +652 -0
  39. package/dist/src/conductor/bridge.js.map +1 -0
  40. package/dist/src/conductor/notifications.d.ts +59 -0
  41. package/dist/src/conductor/notifications.js +381 -0
  42. package/dist/src/conductor/notifications.js.map +1 -0
  43. package/dist/src/conductor/recovery.d.ts +29 -0
  44. package/dist/src/conductor/recovery.js +198 -0
  45. package/dist/src/conductor/recovery.js.map +1 -0
  46. package/dist/src/conductor/start.d.ts +73 -0
  47. package/dist/src/conductor/start.js +519 -0
  48. package/dist/src/conductor/start.js.map +1 -0
  49. package/dist/src/conductor/waiter.d.ts +88 -0
  50. package/dist/src/conductor/waiter.js +427 -0
  51. package/dist/src/conductor/waiter.js.map +1 -0
  52. package/dist/src/config.d.ts +9 -0
  53. package/dist/src/config.js +40 -0
  54. package/dist/src/config.js.map +1 -0
  55. package/dist/src/dispatch/advice-source.d.ts +61 -0
  56. package/dist/src/dispatch/advice-source.js +198 -0
  57. package/dist/src/dispatch/advice-source.js.map +1 -0
  58. package/dist/src/dispatch/advice-store.d.ts +41 -0
  59. package/dist/src/dispatch/advice-store.js +201 -0
  60. package/dist/src/dispatch/advice-store.js.map +1 -0
  61. package/dist/src/dispatch/advice.d.ts +34 -0
  62. package/dist/src/dispatch/advice.js +371 -0
  63. package/dist/src/dispatch/advice.js.map +1 -0
  64. package/dist/src/dispatch/authority.d.ts +124 -0
  65. package/dist/src/dispatch/authority.js +1202 -0
  66. package/dist/src/dispatch/authority.js.map +1 -0
  67. package/dist/src/dispatch/intent.d.ts +66 -0
  68. package/dist/src/dispatch/intent.js +71 -0
  69. package/dist/src/dispatch/intent.js.map +1 -0
  70. package/dist/src/dispatch/runtime-authority.d.ts +42 -0
  71. package/dist/src/dispatch/runtime-authority.js +52 -0
  72. package/dist/src/dispatch/runtime-authority.js.map +1 -0
  73. package/dist/src/doctor.d.ts +20 -0
  74. package/dist/src/doctor.js +85 -0
  75. package/dist/src/doctor.js.map +1 -0
  76. package/dist/src/domain/records.d.ts +44 -0
  77. package/dist/src/domain/records.js +138 -0
  78. package/dist/src/domain/records.js.map +1 -0
  79. package/dist/src/domain/role-policy.d.ts +67 -0
  80. package/dist/src/domain/role-policy.js +547 -0
  81. package/dist/src/domain/role-policy.js.map +1 -0
  82. package/dist/src/events/bus.d.ts +32 -0
  83. package/dist/src/events/bus.js +115 -0
  84. package/dist/src/events/bus.js.map +1 -0
  85. package/dist/src/events/kinds.d.ts +126 -0
  86. package/dist/src/events/kinds.js +14 -0
  87. package/dist/src/events/kinds.js.map +1 -0
  88. package/dist/src/evidence/immutable-file.d.ts +12 -0
  89. package/dist/src/evidence/immutable-file.js +141 -0
  90. package/dist/src/evidence/immutable-file.js.map +1 -0
  91. package/dist/src/evidence/store.d.ts +125 -0
  92. package/dist/src/evidence/store.js +1189 -0
  93. package/dist/src/evidence/store.js.map +1 -0
  94. package/dist/src/evidence/verdict.d.ts +17 -0
  95. package/dist/src/evidence/verdict.js +23 -0
  96. package/dist/src/evidence/verdict.js.map +1 -0
  97. package/dist/src/generation/agent.d.ts +5 -0
  98. package/dist/src/generation/agent.js +34 -0
  99. package/dist/src/generation/agent.js.map +1 -0
  100. package/dist/src/generation/frontmatter.d.ts +7 -0
  101. package/dist/src/generation/frontmatter.js +33 -0
  102. package/dist/src/generation/frontmatter.js.map +1 -0
  103. package/dist/src/github/adapter.d.ts +44 -0
  104. package/dist/src/github/adapter.js +592 -0
  105. package/dist/src/github/adapter.js.map +1 -0
  106. package/dist/src/github/dispatch-eligibility.d.ts +9 -0
  107. package/dist/src/github/dispatch-eligibility.js +94 -0
  108. package/dist/src/github/dispatch-eligibility.js.map +1 -0
  109. package/dist/src/github/projector.d.ts +23 -0
  110. package/dist/src/github/projector.js +181 -0
  111. package/dist/src/github/projector.js.map +1 -0
  112. package/dist/src/github/review.d.ts +39 -0
  113. package/dist/src/github/review.js +1450 -0
  114. package/dist/src/github/review.js.map +1 -0
  115. package/dist/src/github/sync.d.ts +21 -0
  116. package/dist/src/github/sync.js +518 -0
  117. package/dist/src/github/sync.js.map +1 -0
  118. package/dist/src/github/types.d.ts +134 -0
  119. package/dist/src/github/types.js +11 -0
  120. package/dist/src/github/types.js.map +1 -0
  121. package/dist/src/goals/artifact-store.d.ts +31 -0
  122. package/dist/src/goals/artifact-store.js +241 -0
  123. package/dist/src/goals/artifact-store.js.map +1 -0
  124. package/dist/src/goals/authority.d.ts +112 -0
  125. package/dist/src/goals/authority.js +621 -0
  126. package/dist/src/goals/authority.js.map +1 -0
  127. package/dist/src/goals/native-snapshot.d.ts +12 -0
  128. package/dist/src/goals/native-snapshot.js +53 -0
  129. package/dist/src/goals/native-snapshot.js.map +1 -0
  130. package/dist/src/goals/runtime-authority.d.ts +7 -0
  131. package/dist/src/goals/runtime-authority.js +20 -0
  132. package/dist/src/goals/runtime-authority.js.map +1 -0
  133. package/dist/src/health/release-assessment.d.ts +11 -0
  134. package/dist/src/health/release-assessment.js +83 -0
  135. package/dist/src/health/release-assessment.js.map +1 -0
  136. package/dist/src/health/release-policy.d.ts +16 -0
  137. package/dist/src/health/release-policy.js +40 -0
  138. package/dist/src/health/release-policy.js.map +1 -0
  139. package/dist/src/hooks/native.d.ts +30 -0
  140. package/dist/src/hooks/native.js +495 -0
  141. package/dist/src/hooks/native.js.map +1 -0
  142. package/dist/src/hooks/provider-runtime.d.ts +55 -0
  143. package/dist/src/hooks/provider-runtime.js +479 -0
  144. package/dist/src/hooks/provider-runtime.js.map +1 -0
  145. package/dist/src/hooks/provider.d.ts +102 -0
  146. package/dist/src/hooks/provider.js +358 -0
  147. package/dist/src/hooks/provider.js.map +1 -0
  148. package/dist/src/live/contact-journal.d.ts +73 -0
  149. package/dist/src/live/contact-journal.js +439 -0
  150. package/dist/src/live/contact-journal.js.map +1 -0
  151. package/dist/src/live-probes/gh-adapter.d.ts +28 -0
  152. package/dist/src/live-probes/gh-adapter.js +188 -0
  153. package/dist/src/live-probes/gh-adapter.js.map +1 -0
  154. package/dist/src/live-probes/github-queue.d.ts +1 -0
  155. package/dist/src/live-probes/github-queue.js +2 -0
  156. package/dist/src/live-probes/github-queue.js.map +1 -0
  157. package/dist/src/notifications/slack-request.d.ts +13 -0
  158. package/dist/src/notifications/slack-request.js +40 -0
  159. package/dist/src/notifications/slack-request.js.map +1 -0
  160. package/dist/src/outbox/processor.d.ts +27 -0
  161. package/dist/src/outbox/processor.js +63 -0
  162. package/dist/src/outbox/processor.js.map +1 -0
  163. package/dist/src/outbox/store.d.ts +158 -0
  164. package/dist/src/outbox/store.js +834 -0
  165. package/dist/src/outbox/store.js.map +1 -0
  166. package/dist/src/package-root.d.ts +1 -0
  167. package/dist/src/package-root.js +26 -0
  168. package/dist/src/package-root.js.map +1 -0
  169. package/dist/src/project.d.ts +12 -0
  170. package/dist/src/project.js +60 -0
  171. package/dist/src/project.js.map +1 -0
  172. package/dist/src/projection/generated-file.d.ts +15 -0
  173. package/dist/src/projection/generated-file.js +72 -0
  174. package/dist/src/projection/generated-file.js.map +1 -0
  175. package/dist/src/projection/project-workspace.d.ts +11 -0
  176. package/dist/src/projection/project-workspace.js +94 -0
  177. package/dist/src/projection/project-workspace.js.map +1 -0
  178. package/dist/src/projection/registry.d.ts +15 -0
  179. package/dist/src/projection/registry.js +59 -0
  180. package/dist/src/projection/registry.js.map +1 -0
  181. package/dist/src/projection/safe-path.d.ts +4 -0
  182. package/dist/src/projection/safe-path.js +106 -0
  183. package/dist/src/projection/safe-path.js.map +1 -0
  184. package/dist/src/providers/claude-interactive.d.ts +38 -0
  185. package/dist/src/providers/claude-interactive.js +287 -0
  186. package/dist/src/providers/claude-interactive.js.map +1 -0
  187. package/dist/src/providers/claude-structured-bridge.d.ts +32 -0
  188. package/dist/src/providers/claude-structured-bridge.js +215 -0
  189. package/dist/src/providers/claude-structured-bridge.js.map +1 -0
  190. package/dist/src/providers/claude-structured-completion.d.ts +23 -0
  191. package/dist/src/providers/claude-structured-completion.js +229 -0
  192. package/dist/src/providers/claude-structured-completion.js.map +1 -0
  193. package/dist/src/providers/claude-structured-pane.d.ts +23 -0
  194. package/dist/src/providers/claude-structured-pane.js +156 -0
  195. package/dist/src/providers/claude-structured-pane.js.map +1 -0
  196. package/dist/src/providers/claude-structured-store.d.ts +48 -0
  197. package/dist/src/providers/claude-structured-store.js +212 -0
  198. package/dist/src/providers/claude-structured-store.js.map +1 -0
  199. package/dist/src/providers/claude-structured.d.ts +56 -0
  200. package/dist/src/providers/claude-structured.js +250 -0
  201. package/dist/src/providers/claude-structured.js.map +1 -0
  202. package/dist/src/providers/codex-exec.d.ts +29 -0
  203. package/dist/src/providers/codex-exec.js +127 -0
  204. package/dist/src/providers/codex-exec.js.map +1 -0
  205. package/dist/src/providers/grading.d.ts +2 -0
  206. package/dist/src/providers/grading.js +20 -0
  207. package/dist/src/providers/grading.js.map +1 -0
  208. package/dist/src/providers/native-dispatch.d.ts +24 -0
  209. package/dist/src/providers/native-dispatch.js +34 -0
  210. package/dist/src/providers/native-dispatch.js.map +1 -0
  211. package/dist/src/providers/preflight.d.ts +65 -0
  212. package/dist/src/providers/preflight.js +256 -0
  213. package/dist/src/providers/preflight.js.map +1 -0
  214. package/dist/src/providers/process.d.ts +18 -0
  215. package/dist/src/providers/process.js +21 -0
  216. package/dist/src/providers/process.js.map +1 -0
  217. package/dist/src/queue/model.d.ts +37 -0
  218. package/dist/src/queue/model.js +51 -0
  219. package/dist/src/queue/model.js.map +1 -0
  220. package/dist/src/queue/service.d.ts +1 -0
  221. package/dist/src/queue/service.js +2 -0
  222. package/dist/src/queue/service.js.map +1 -0
  223. package/dist/src/reflections/store.d.ts +21 -0
  224. package/dist/src/reflections/store.js +60 -0
  225. package/dist/src/reflections/store.js.map +1 -0
  226. package/dist/src/reports/daily-trust.d.ts +11 -0
  227. package/dist/src/reports/daily-trust.js +98 -0
  228. package/dist/src/reports/daily-trust.js.map +1 -0
  229. package/dist/src/runs/controller.d.ts +187 -0
  230. package/dist/src/runs/controller.js +1182 -0
  231. package/dist/src/runs/controller.js.map +1 -0
  232. package/dist/src/runtime/composition.d.ts +9 -0
  233. package/dist/src/runtime/composition.js +35 -0
  234. package/dist/src/runtime/composition.js.map +1 -0
  235. package/dist/src/runtime/endurance-authorities.d.ts +44 -0
  236. package/dist/src/runtime/endurance-authorities.js +101 -0
  237. package/dist/src/runtime/endurance-authorities.js.map +1 -0
  238. package/dist/src/runtime/endurance-policy.d.ts +12 -0
  239. package/dist/src/runtime/endurance-policy.js +13 -0
  240. package/dist/src/runtime/endurance-policy.js.map +1 -0
  241. package/dist/src/runtime/lock.d.ts +63 -0
  242. package/dist/src/runtime/lock.js +577 -0
  243. package/dist/src/runtime/lock.js.map +1 -0
  244. package/dist/src/runtime/process-identity.d.ts +10 -0
  245. package/dist/src/runtime/process-identity.js +43 -0
  246. package/dist/src/runtime/process-identity.js.map +1 -0
  247. package/dist/src/runtime/quota.d.ts +130 -0
  248. package/dist/src/runtime/quota.js +590 -0
  249. package/dist/src/runtime/quota.js.map +1 -0
  250. package/dist/src/runtime/record-store.d.ts +45 -0
  251. package/dist/src/runtime/record-store.js +272 -0
  252. package/dist/src/runtime/record-store.js.map +1 -0
  253. package/dist/src/runtime/schema.d.ts +28 -0
  254. package/dist/src/runtime/schema.js +194 -0
  255. package/dist/src/runtime/schema.js.map +1 -0
  256. package/dist/src/runtime/stable-id.d.ts +3 -0
  257. package/dist/src/runtime/stable-id.js +27 -0
  258. package/dist/src/runtime/stable-id.js.map +1 -0
  259. package/dist/src/runtime/system-clock.d.ts +8 -0
  260. package/dist/src/runtime/system-clock.js +35 -0
  261. package/dist/src/runtime/system-clock.js.map +1 -0
  262. package/dist/src/schedule/service-plan.d.ts +58 -0
  263. package/dist/src/schedule/service-plan.js +327 -0
  264. package/dist/src/schedule/service-plan.js.map +1 -0
  265. package/dist/src/schedule/user-installation.d.ts +62 -0
  266. package/dist/src/schedule/user-installation.js +701 -0
  267. package/dist/src/schedule/user-installation.js.map +1 -0
  268. package/dist/src/schedule/user-plan.d.ts +47 -0
  269. package/dist/src/schedule/user-plan.js +286 -0
  270. package/dist/src/schedule/user-plan.js.map +1 -0
  271. package/dist/src/scheduler/minute.d.ts +31 -0
  272. package/dist/src/scheduler/minute.js +441 -0
  273. package/dist/src/scheduler/minute.js.map +1 -0
  274. package/dist/src/schemas/registry.d.ts +10 -0
  275. package/dist/src/schemas/registry.js +36 -0
  276. package/dist/src/schemas/registry.js.map +1 -0
  277. package/dist/src/security/secret-scan.d.ts +50 -0
  278. package/dist/src/security/secret-scan.js +384 -0
  279. package/dist/src/security/secret-scan.js.map +1 -0
  280. package/dist/src/sessions/registry.d.ts +89 -0
  281. package/dist/src/sessions/registry.js +443 -0
  282. package/dist/src/sessions/registry.js.map +1 -0
  283. package/dist/src/state.d.ts +27 -0
  284. package/dist/src/state.js +235 -0
  285. package/dist/src/state.js.map +1 -0
  286. package/dist/src/status/hud.d.ts +37 -0
  287. package/dist/src/status/hud.js +88 -0
  288. package/dist/src/status/hud.js.map +1 -0
  289. package/dist/src/team/plan.d.ts +31 -0
  290. package/dist/src/team/plan.js +184 -0
  291. package/dist/src/team/plan.js.map +1 -0
  292. package/dist/src/team/projection.d.ts +24 -0
  293. package/dist/src/team/projection.js +79 -0
  294. package/dist/src/team/projection.js.map +1 -0
  295. package/dist/src/tickets/admission.d.ts +34 -0
  296. package/dist/src/tickets/admission.js +169 -0
  297. package/dist/src/tickets/admission.js.map +1 -0
  298. package/dist/src/tickets/materializer.d.ts +1 -0
  299. package/dist/src/tickets/materializer.js +2 -0
  300. package/dist/src/tickets/materializer.js.map +1 -0
  301. package/dist/src/tickets/runtime-authority.d.ts +32 -0
  302. package/dist/src/tickets/runtime-authority.js +46 -0
  303. package/dist/src/tickets/runtime-authority.js.map +1 -0
  304. package/dist/src/tickets/store.d.ts +221 -0
  305. package/dist/src/tickets/store.js +2025 -0
  306. package/dist/src/tickets/store.js.map +1 -0
  307. package/dist/src/tracer/one-ticket.d.ts +58 -0
  308. package/dist/src/tracer/one-ticket.js +578 -0
  309. package/dist/src/tracer/one-ticket.js.map +1 -0
  310. package/dist/src/trust/assessment.d.ts +24 -0
  311. package/dist/src/trust/assessment.js +124 -0
  312. package/dist/src/trust/assessment.js.map +1 -0
  313. package/dist/src/trust/runs.d.ts +13 -0
  314. package/dist/src/trust/runs.js +46 -0
  315. package/dist/src/trust/runs.js.map +1 -0
  316. package/dist/src/types.d.ts +6 -0
  317. package/dist/src/types.js +2 -0
  318. package/dist/src/types.js.map +1 -0
  319. package/dist/src/utils/fs.d.ts +13 -0
  320. package/dist/src/utils/fs.js +188 -0
  321. package/dist/src/utils/fs.js.map +1 -0
  322. package/dist/src/verification/authority.d.ts +28 -0
  323. package/dist/src/verification/authority.js +153 -0
  324. package/dist/src/verification/authority.js.map +1 -0
  325. package/dist/src/verification/grade-source.d.ts +17 -0
  326. package/dist/src/verification/grade-source.js +30 -0
  327. package/dist/src/verification/grade-source.js.map +1 -0
  328. package/dist/src/verification/plan.d.ts +20 -0
  329. package/dist/src/verification/plan.js +128 -0
  330. package/dist/src/verification/plan.js.map +1 -0
  331. package/dist/src/verification/protected-runner.d.ts +50 -0
  332. package/dist/src/verification/protected-runner.js +171 -0
  333. package/dist/src/verification/protected-runner.js.map +1 -0
  334. package/dist/src/verification/result-gate-store.d.ts +72 -0
  335. package/dist/src/verification/result-gate-store.js +193 -0
  336. package/dist/src/verification/result-gate-store.js.map +1 -0
  337. package/dist/src/verification/result-gate.d.ts +41 -0
  338. package/dist/src/verification/result-gate.js +731 -0
  339. package/dist/src/verification/result-gate.js.map +1 -0
  340. package/dist/src/verification/runner.d.ts +38 -0
  341. package/dist/src/verification/runner.js +143 -0
  342. package/dist/src/verification/runner.js.map +1 -0
  343. package/dist/src/verification/sandbox.d.ts +16 -0
  344. package/dist/src/verification/sandbox.js +199 -0
  345. package/dist/src/verification/sandbox.js.map +1 -0
  346. package/dist/src/verify.d.ts +8 -0
  347. package/dist/src/verify.js +35 -0
  348. package/dist/src/verify.js.map +1 -0
  349. package/dist/src/watchdog/reconcile.d.ts +42 -0
  350. package/dist/src/watchdog/reconcile.js +440 -0
  351. package/dist/src/watchdog/reconcile.js.map +1 -0
  352. package/dist/src/workers/bridge.d.ts +35 -0
  353. package/dist/src/workers/bridge.js +426 -0
  354. package/dist/src/workers/bridge.js.map +1 -0
  355. package/dist/src/workers/delivery.d.ts +31 -0
  356. package/dist/src/workers/delivery.js +876 -0
  357. package/dist/src/workers/delivery.js.map +1 -0
  358. package/dist/src/workers/panes.d.ts +32 -0
  359. package/dist/src/workers/panes.js +212 -0
  360. package/dist/src/workers/panes.js.map +1 -0
  361. package/dist/src/workers/registry.d.ts +80 -0
  362. package/dist/src/workers/registry.js +351 -0
  363. package/dist/src/workers/registry.js.map +1 -0
  364. package/dist/src/workers/runtime.d.ts +44 -0
  365. package/dist/src/workers/runtime.js +21 -0
  366. package/dist/src/workers/runtime.js.map +1 -0
  367. package/dist/src/worktrees/manager.d.ts +35 -0
  368. package/dist/src/worktrees/manager.js +315 -0
  369. package/dist/src/worktrees/manager.js.map +1 -0
  370. package/docs/ARCHITECTURE.md +323 -0
  371. package/docs/PLAN.md +216 -0
  372. package/docs/REFERENCES.md +42 -0
  373. package/docs/ROADMAP.md +327 -0
  374. package/docs/SECURITY.md +242 -0
  375. package/docs/adr/0001-tracer-first-file-backed-certification.md +41 -0
  376. package/docs/adr/0002-conductor-first-typed-dispatch.md +102 -0
  377. package/docs/adr/0003-mandatory-pre-dispatch-advice.md +59 -0
  378. package/docs/compatibility/claude-code-2.1.207.md +25 -0
  379. package/loop/contract.md +102 -0
  380. package/package.json +54 -0
  381. package/schemas/advisor-evidence.schema.json +54 -0
  382. package/schemas/advisor-output.schema.json +31 -0
  383. package/schemas/conductor-activity-payload.schema.json +38 -0
  384. package/schemas/conductor-decision.schema.json +29 -0
  385. package/schemas/conductor-dispatch-intent.schema.json +71 -0
  386. package/schemas/conductor-dispatch-preparation.schema.json +46 -0
  387. package/schemas/conductor-dispatch-proposal.schema.json +82 -0
  388. package/schemas/conductor-goal-proposal.schema.json +30 -0
  389. package/schemas/conductor-manage-worker.schema.json +17 -0
  390. package/schemas/conductor-notification-payload.schema.json +39 -0
  391. package/schemas/conductor-process-result.schema.json +14 -0
  392. package/schemas/conductor-route.schema.json +51 -0
  393. package/schemas/conductor-start-team.schema.json +16 -0
  394. package/schemas/conductor-waiter-payload.schema.json +68 -0
  395. package/schemas/deterministic-check.schema.json +107 -0
  396. package/schemas/dispatch-acknowledgement.schema.json +15 -0
  397. package/schemas/dispatch-advice-payload.schema.json +64 -0
  398. package/schemas/dispatch-payload.schema.json +131 -0
  399. package/schemas/dispatch-result.schema.json +28 -0
  400. package/schemas/endurance-policy.schema.json +16 -0
  401. package/schemas/events/dispatch-delivery-requested.schema.json +17 -0
  402. package/schemas/events/github-observed.schema.json +28 -0
  403. package/schemas/events/native-hook-observed.schema.json +35 -0
  404. package/schemas/events/provider-observed.schema.json +64 -0
  405. package/schemas/events/run-heartbeat-due.schema.json +10 -0
  406. package/schemas/events/slack-notification-requested.schema.json +14 -0
  407. package/schemas/events/structured-role-completed.schema.json +54 -0
  408. package/schemas/events/ticket-resume-due.schema.json +28 -0
  409. package/schemas/events/watchdog-lease-expired.schema.json +14 -0
  410. package/schemas/events/watchdog-outbox-stalled.schema.json +12 -0
  411. package/schemas/events/watchdog-session-stalled.schema.json +11 -0
  412. package/schemas/evidence-record.schema.json +37 -0
  413. package/schemas/executor-result.schema.json +17 -0
  414. package/schemas/github-observation.schema.json +33 -0
  415. package/schemas/github-watermark.schema.json +66 -0
  416. package/schemas/goal-artifact.schema.json +19 -0
  417. package/schemas/goal-create-input.schema.json +14 -0
  418. package/schemas/goal-executor-result.schema.json +18 -0
  419. package/schemas/goal-payload.schema.json +95 -0
  420. package/schemas/grader-output.schema.json +33 -0
  421. package/schemas/lease-expiry.schema.json +19 -0
  422. package/schemas/live-disclosure.schema.json +16 -0
  423. package/schemas/live-outcome.schema.json +52 -0
  424. package/schemas/native-goal-snapshot.schema.json +27 -0
  425. package/schemas/provider-observed.schema.json +64 -0
  426. package/schemas/quota-observation.schema.json +72 -0
  427. package/schemas/reflection-evidence.schema.json +61 -0
  428. package/schemas/reflection.schema.json +13 -0
  429. package/schemas/regression-request.schema.json +54 -0
  430. package/schemas/release-assessment.schema.json +34 -0
  431. package/schemas/release-policy.schema.json +30 -0
  432. package/schemas/result-gate-payload.schema.json +244 -0
  433. package/schemas/review-delivery-payload.schema.json +42 -0
  434. package/schemas/role-policy.schema.json +111 -0
  435. package/schemas/run-abort-input.schema.json +11 -0
  436. package/schemas/run-completion-proof.schema.json +62 -0
  437. package/schemas/run-ledger-entry.schema.json +78 -0
  438. package/schemas/run-manifest.schema.json +74 -0
  439. package/schemas/run-start-intent.schema.json +74 -0
  440. package/schemas/runtime-record.schema.json +111 -0
  441. package/schemas/service-plan.schema.json +35 -0
  442. package/schemas/session-payload.schema.json +80 -0
  443. package/schemas/structured-role-read-request.schema.json +14 -0
  444. package/schemas/structured-role-request.schema.json +44 -0
  445. package/schemas/structured-role-result.schema.json +38 -0
  446. package/schemas/structured-role-submit-result.schema.json +15 -0
  447. package/schemas/team-checkpoint.schema.json +42 -0
  448. package/schemas/team-plan.schema.json +30 -0
  449. package/schemas/ticket-content.schema.json +38 -0
  450. package/schemas/ticket-queue-payload.schema.json +191 -0
  451. package/schemas/trust-report.schema.json +68 -0
  452. package/schemas/trust-run.schema.json +47 -0
  453. package/schemas/ultragoal-checkpoint.schema.json +49 -0
  454. package/schemas/user-schedule-installation-v1.schema.json +32 -0
  455. package/schemas/user-schedule-installation.schema.json +38 -0
  456. package/schemas/user-schedule-plan.schema.json +39 -0
  457. package/schemas/verification-plan-evidence.schema.json +37 -0
  458. package/schemas/worker-acknowledge-dispatch.schema.json +12 -0
  459. package/schemas/worker-payload.schema.json +47 -0
  460. package/schemas/worker-read-dispatch.schema.json +11 -0
  461. package/schemas/worker-record-dispatch-result.schema.json +14 -0
@@ -0,0 +1,1450 @@
1
+ import { createHash } from "node:crypto";
2
+ import { join } from "node:path";
3
+ import { isDeepStrictEqual } from "node:util";
4
+ import { assertActorId, assertOpaqueId, assertRecordId, assertTransitionNonce, } from "../domain/records.js";
5
+ import { assertFileInside, openStableDirectory, readImmutableFile, writeImmutableFile, } from "../evidence/immutable-file.js";
6
+ import { discloseLiveContact, listLiveContacts, readLiveContact, readLiveDeliveryAuthorization, } from "../live/contact-journal.js";
7
+ import { runProcess } from "../providers/process.js";
8
+ import { canonicalTimestamp } from "../queue/model.js";
9
+ import { createRecordStore } from "../runtime/record-store.js";
10
+ import { assertSchema, loadJsonSchema } from "../runtime/schema.js";
11
+ import { createStableId } from "../runtime/stable-id.js";
12
+ import { runSecretScan, readSecretScan, } from "../security/secret-scan.js";
13
+ import { readJsonFileNoFollow, isRecord, validateExistingDirectory } from "../utils/fs.js";
14
+ import { createResultGateStore } from "../verification/result-gate-store.js";
15
+ import { createRuntimeWorkerAuthorities } from "../workers/runtime.js";
16
+ import { deriveWorktreeId } from "../dispatch/authority.js";
17
+ import { readGoal } from "../goals/authority.js";
18
+ import { parseTicketWorktreeRecord } from "../worktrees/manager.js";
19
+ import { normalizeGithubRemoteRepository, normalizeGithubSyncTarget } from "./adapter.js";
20
+ const BASE_BRANCH = "main";
21
+ const MAX_GITHUB_BYTES = 2 * 1024 * 1024;
22
+ const MAX_PROOF_BYTES = 512 * 1024;
23
+ const SHA_PATTERN = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u;
24
+ const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
25
+ const BRANCH_PATTERN = /^oms-ticket-[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/u;
26
+ const REQUIRED_CHECK_PATTERN = /^[A-Za-z0-9][A-Za-z0-9 ._:/-]{0,127}$/u;
27
+ export const OMS_REVIEW_REQUIRED_CHECK = "OMS deterministic verification";
28
+ const REVIEW_QUERY = `query OmsReviewRead($owner: String!, $name: String!, $branchRef: String!) {
29
+ repository(owner: $owner, name: $name) {
30
+ id
31
+ ref(qualifiedName: $branchRef) { target { oid } }
32
+ pullRequests(first: 100, states: [OPEN], orderBy: {field: UPDATED_AT, direction: DESC}) {
33
+ nodes {
34
+ id number url state isDraft headRefName baseRefName
35
+ commits(last: 1) { totalCount nodes { commit { oid } } }
36
+ }
37
+ }
38
+ }
39
+ rateLimit { remaining resetAt }
40
+ }`;
41
+ const CREATE_PR_MUTATION = `mutation OmsCreateDraftPullRequest(
42
+ $repositoryId: ID!, $baseRefName: String!, $headRefName: String!, $title: String!, $body: String!
43
+ ) {
44
+ createPullRequest(input: {
45
+ repositoryId: $repositoryId,
46
+ baseRefName: $baseRefName,
47
+ headRefName: $headRefName,
48
+ title: $title,
49
+ body: $body,
50
+ draft: true
51
+ }) {
52
+ pullRequest {
53
+ id number url state isDraft headRefName baseRefName
54
+ commits(last: 1) { totalCount nodes { commit { oid } } }
55
+ }
56
+ }
57
+ }`;
58
+ const LEGAL_EDGES = Object.freeze({
59
+ planned: ["push-pending", "pushed", "blocked"],
60
+ "push-pending": ["push-pending", "pushed", "blocked"],
61
+ pushed: ["pr-pending", "awaiting-check", "blocked"],
62
+ "pr-pending": ["pr-pending", "awaiting-check", "blocked"],
63
+ "awaiting-check": ["ready", "blocked"],
64
+ ready: [],
65
+ blocked: [],
66
+ });
67
+ export function createReviewDeliveryService(options) {
68
+ const packageRoot = validateExistingDirectory(options.packageRoot, "review package root");
69
+ const configRoot = validateExistingDirectory(options.configRoot ?? packageRoot, "review configuration root");
70
+ const workspaceRoot = validateExistingDirectory(options.workspaceRoot, "review workspace root");
71
+ const stateRoot = validateExistingDirectory(options.stateRoot, "review state root");
72
+ const requiredCheck = validateRequiredCheck(options.requiredCheckName);
73
+ const clock = options.clock ?? (() => new Date().toISOString());
74
+ const env = options.env ?? process.env;
75
+ const runner = options.runner ?? runProcess;
76
+ const secretScanner = options.secretScanner ?? runSecretScan;
77
+ const runtime = options.runtime ?? createRuntimeWorkerAuthorities({
78
+ packageRoot,
79
+ configRoot,
80
+ workspaceRoot,
81
+ stateRoot,
82
+ actorId: "oms-conductor",
83
+ clock,
84
+ });
85
+ const gates = createResultGateStore({ packageRoot, stateRoot });
86
+ const deliveries = createReviewDeliveryStore({ packageRoot, stateRoot, clock });
87
+ function advance(dispatchId, result) {
88
+ if (runtime.tickets.lifecycle.read(result.ticket_id).payload.source !== "github")
89
+ return null;
90
+ const canonical = captureCanonicalSubject(dispatchId, result);
91
+ const deliveryId = createStableId("review", canonical.subject.dispatch_id, canonical.subject.expected_commit);
92
+ let delivery = deliveries.readIfExists(deliveryId);
93
+ if (!delivery)
94
+ delivery = planDelivery(deliveryId, canonical);
95
+ assertReplayBinding(delivery, canonical);
96
+ for (let step = 0; step < 8; step += 1) {
97
+ if (delivery.state === "ready") {
98
+ assertReadyAttachments(delivery);
99
+ return toProgress(delivery);
100
+ }
101
+ if (delivery.state === "blocked")
102
+ return toProgress(delivery);
103
+ if (delivery.state === "planned") {
104
+ const advanced = advancePush(delivery);
105
+ delivery = advanced.record;
106
+ if (advanced.waiting)
107
+ return toProgress(delivery);
108
+ }
109
+ else if (delivery.state === "push-pending")
110
+ delivery = recoverPush(delivery);
111
+ else if (delivery.state === "pushed") {
112
+ const advanced = advancePullRequest(delivery);
113
+ delivery = advanced.record;
114
+ if (advanced.waiting)
115
+ return toProgress(delivery);
116
+ }
117
+ else if (delivery.state === "pr-pending")
118
+ delivery = recoverPullRequest(delivery);
119
+ else if (delivery.state === "awaiting-check") {
120
+ const completed = finishAfterTypedEvents(delivery, canonical.issueScanContent);
121
+ if (!completed)
122
+ return toProgress(delivery);
123
+ delivery = completed;
124
+ }
125
+ }
126
+ return toProgress(delivery);
127
+ }
128
+ function captureCanonicalSubject(dispatchId, supplied) {
129
+ const dispatch = runtime.dispatch.read(assertRecordId(dispatchId, "review dispatch id"));
130
+ const gate = gates.read(assertRecordId(supplied.gate_id, "review result-gate id"));
131
+ const ticket = runtime.tickets.lifecycle.read(gate.payload.subject.ticket_id);
132
+ if (supplied.status !== "completed"
133
+ || supplied.gate_state !== "finalized"
134
+ || supplied.ticket_state !== "completed"
135
+ || supplied.reason !== null
136
+ || gate.state !== "finalized"
137
+ || gate.payload.outcome !== "completed"
138
+ || gate.payload.subject.dispatch_id !== dispatch.record_id
139
+ || supplied.ticket_id !== ticket.record_id
140
+ || supplied.deterministic_evidence === null
141
+ || supplied.grade_evidence === null
142
+ || !sameReference(supplied.deterministic_evidence, gate.payload.deterministic_evidence)
143
+ || !sameReference(supplied.grade_evidence, gate.payload.grade_evidence))
144
+ throw new Error("review delivery requires the canonical completed protected result");
145
+ const subject = gate.payload.subject;
146
+ const goal = readGoal(runtime.goals.authority, subject.goal_id);
147
+ const worker = runtime.workers.read(subject.dispatch_id);
148
+ const session = runtime.sessions.read(subject.session_id);
149
+ const worktree = parseTicketWorktreeRecord(readJsonFileNoFollow(join(stateRoot, "worktree-records", `${subject.ticket_id}.json`), 64 * 1024), "review worktree record");
150
+ const deterministic = runtime.tickets.evidenceStore.read(supplied.deterministic_evidence);
151
+ const grade = runtime.tickets.evidenceStore.read(supplied.grade_evidence);
152
+ if (dispatch.state !== "resolved"
153
+ || dispatch.payload.resolution !== "done"
154
+ || dispatch.payload.result?.status !== "done"
155
+ || dispatch.version !== subject.dispatch_version
156
+ || dispatch.payload.goal_id !== subject.goal_id
157
+ || dispatch.payload.goal_version !== subject.goal_version
158
+ || dispatch.payload.ticket_id !== subject.ticket_id
159
+ || dispatch.payload.attempt !== subject.attempt
160
+ || dispatch.payload.lease_token !== subject.lease_token
161
+ || dispatch.payload.worker_record_id !== subject.worker_record_id
162
+ || dispatch.payload.worker_id !== subject.worker_id
163
+ || dispatch.payload.session_id !== subject.session_id
164
+ || dispatch.payload.worktree_id !== subject.worktree_id
165
+ || dispatch.payload.worktree_path !== subject.worktree_root
166
+ || dispatch.payload.worktree_branch !== subject.worktree_branch
167
+ || dispatch.payload.worktree_base_commit !== subject.worktree_base_commit
168
+ || dispatch.payload.result.worktree_commit !== subject.expected_commit
169
+ || ticket.state !== "completed"
170
+ || ticket.attempt !== subject.attempt
171
+ || ticket.payload.source !== "github"
172
+ || !sameReference(ticket.payload.verification_evidence, supplied.deterministic_evidence)
173
+ || !sameReference(ticket.payload.grade_evidence, supplied.grade_evidence)
174
+ || goal.version !== subject.goal_version
175
+ || !["active", "completed"].includes(goal.state)
176
+ || goal.payload.repository_scope !== workspaceRoot
177
+ || worker.state !== "done"
178
+ || worker.record_id !== subject.worker_record_id
179
+ || worker.payload.external_worker_id !== subject.worker_id
180
+ || worker.payload.session_id !== subject.session_id
181
+ || worker.payload.ticket_id !== subject.ticket_id
182
+ || session.payload.ticket_id !== subject.ticket_id
183
+ || session.payload.native_session_ref !== subject.session_id
184
+ || worktree.ticket_id !== subject.ticket_id
185
+ || worktree.repository_root !== workspaceRoot
186
+ || worktree.worktree_root !== subject.worktree_root
187
+ || worktree.branch !== subject.worktree_branch
188
+ || worktree.base_commit !== subject.worktree_base_commit
189
+ || deriveWorktreeId(worktree) !== subject.worktree_id
190
+ || worktree.requested_base !== BASE_BRANCH
191
+ || !BRANCH_PATTERN.test(worktree.branch)
192
+ || deterministic.evidence_kind !== "deterministic-check"
193
+ || deterministic.ticket_id !== subject.ticket_id
194
+ || deterministic.payload.certification !== "live"
195
+ || deterministic.payload.passed !== true
196
+ || grade.evidence_kind !== "grader-output"
197
+ || grade.ticket_id !== subject.ticket_id
198
+ || grade.payload.verdict !== "pass"
199
+ || !sameReference(grade.payload.deterministic_evidence, supplied.deterministic_evidence))
200
+ throw new Error("review delivery authority binding no longer matches the protected result chain");
201
+ if (!isDeepStrictEqual(deterministic.payload.binding, protectedBinding(subject))) {
202
+ throw new Error("deterministic evidence binding does not match the result-gate subject");
203
+ }
204
+ const content = runtime.tickets.materializer.readContent({
205
+ ticket_id: ticket.record_id,
206
+ sha256: ticket.payload.content_sha256,
207
+ file_name: ticket.payload.content_file,
208
+ });
209
+ if (content.repository === null || content.issue_node_id === null || content.url === null) {
210
+ throw new Error("review delivery requires one canonical GitHub ticket");
211
+ }
212
+ const repository = normalizeGithubSyncTarget(content.repository).repository;
213
+ const issueNumber = parseIssueNumber(content.url, repository);
214
+ const authorization = readLiveDeliveryAuthorization(stateRoot);
215
+ if (!authorization || authorization.repository !== repository) {
216
+ throw new Error("review delivery lacks matching human live authorization");
217
+ }
218
+ return {
219
+ subject: Object.freeze({
220
+ goal_id: subject.goal_id,
221
+ goal_version: subject.goal_version,
222
+ dispatch_id: subject.dispatch_id,
223
+ dispatch_version: subject.dispatch_version,
224
+ ticket_id: subject.ticket_id,
225
+ attempt: subject.attempt,
226
+ lease_token: subject.lease_token,
227
+ worker_record_id: subject.worker_record_id,
228
+ worker_id: subject.worker_id,
229
+ session_id: subject.session_id,
230
+ worktree_id: subject.worktree_id,
231
+ worktree_root: subject.worktree_root,
232
+ worktree_branch: subject.worktree_branch,
233
+ worktree_base_commit: subject.worktree_base_commit,
234
+ expected_commit: subject.expected_commit,
235
+ deterministic_evidence: supplied.deterministic_evidence,
236
+ grade_evidence: supplied.grade_evidence,
237
+ }),
238
+ gate,
239
+ repository,
240
+ issueNumber,
241
+ issueNodeId: content.issue_node_id,
242
+ issueUrlSha256: sha256(content.url),
243
+ sourceRefSha256: ticket.payload.source_ref_sha256,
244
+ issueScanContent: Object.freeze({
245
+ title: content.title,
246
+ body: content.body,
247
+ labels: Object.freeze([...content.labels]),
248
+ url: content.url,
249
+ }),
250
+ authorizationId: authorization.authorization_id,
251
+ conductorSessionId: authorization.conductor_session_id,
252
+ };
253
+ }
254
+ function planDelivery(deliveryId, canonical) {
255
+ const createdAt = canonicalTimestamp(clock(), "review delivery timestamp");
256
+ assertAuthorizedOrigin(canonical.repository, canonical.subject.worktree_root, env, runner);
257
+ assertCommitAncestry(canonical.subject, env, runner);
258
+ const outbound = outboundPlan(canonical.repository, canonical.issueNumber, canonical.issueNodeId, canonical.issueScanContent, canonical.subject, requiredCheck);
259
+ const scan = secretScanner({
260
+ packageRoot,
261
+ stateRoot,
262
+ ticketId: canonical.subject.ticket_id,
263
+ phase: "preflight",
264
+ worktreeRoot: canonical.subject.worktree_root,
265
+ baseCommit: canonical.subject.worktree_base_commit,
266
+ verifiedCommit: canonical.subject.expected_commit,
267
+ outbound,
268
+ env,
269
+ runner,
270
+ clock: () => createdAt,
271
+ });
272
+ assertSecretScan(scan.result, "preflight", canonical.subject);
273
+ return deliveries.create({
274
+ deliveryId,
275
+ at: createdAt,
276
+ payload: {
277
+ delivery_id: deliveryId,
278
+ repository: canonical.repository,
279
+ issue_number: canonical.issueNumber,
280
+ issue_node_id: canonical.issueNodeId,
281
+ issue_url_sha256: canonical.issueUrlSha256,
282
+ source_ref_sha256: canonical.sourceRefSha256,
283
+ subject: canonical.subject,
284
+ result_gate_id: canonical.gate.record_id,
285
+ authorization_id: canonical.authorizationId,
286
+ conductor_session_id: canonical.conductorSessionId,
287
+ base_branch: BASE_BRANCH,
288
+ review_branch: canonical.subject.worktree_branch,
289
+ required_check: requiredCheck,
290
+ created_at: createdAt,
291
+ updated_at: createdAt,
292
+ preflight_secret_scan: scan.reference,
293
+ final_secret_scan: null,
294
+ contacts: [],
295
+ pull_request: null,
296
+ pull_request_event: null,
297
+ check_event: null,
298
+ proof: null,
299
+ blocked_reason: null,
300
+ },
301
+ });
302
+ }
303
+ function advancePush(record) {
304
+ const readback = readRemote(record, "before-push");
305
+ if (readback.snapshot.branch_head_oid !== null) {
306
+ if (readback.snapshot.branch_head_oid !== record.payload.subject.expected_commit) {
307
+ return { record: block(record, "remote review branch does not match the verified commit", readback.receipt), waiting: false };
308
+ }
309
+ return { record: deliveries.transition(record, "branch-already-pushed", "pushed", [readback.receipt]), waiting: false };
310
+ }
311
+ const request = pushRequest(record, env);
312
+ const contact = disclose(record, "git-branch-push", "git-origin", "push", request);
313
+ const claimed = deliveries.claim(record, "push-intent", "push-pending", [
314
+ readback.receipt,
315
+ { phase: "push", operation: "git-branch-push", contact, response_sha256: null },
316
+ ]);
317
+ if (claimed.status === "replayed")
318
+ return { record: claimed.record, waiting: true };
319
+ let pending = claimed.record;
320
+ const result = safeRun(runner, request);
321
+ const responseSha256 = processResultDigest(result);
322
+ pending = deliveries.transition(pending, `push-result-${responseSha256.slice(0, 12)}`, "push-pending", [], (payload) => ({
323
+ ...payload,
324
+ contacts: replacePendingReceipt(payload.contacts, contact, responseSha256),
325
+ }));
326
+ if (processSucceeded(result)) {
327
+ return { record: deliveries.transition(pending, "push-confirmed", "pushed"), waiting: false };
328
+ }
329
+ const recovered = readRemote(pending, "after-ambiguous-push");
330
+ if (recovered.snapshot.branch_head_oid === pending.payload.subject.expected_commit) {
331
+ return { record: deliveries.transition(pending, "push-readback-confirmed", "pushed", [recovered.receipt]), waiting: false };
332
+ }
333
+ if (recovered.snapshot.branch_head_oid !== null) {
334
+ return { record: block(pending, "ambiguous push resolved to a different remote commit", recovered.receipt), waiting: false };
335
+ }
336
+ return { record: pending, waiting: true };
337
+ }
338
+ function recoverPush(record) {
339
+ const readback = readRemote(record, "recover-push");
340
+ if (readback.snapshot.branch_head_oid === record.payload.subject.expected_commit) {
341
+ return deliveries.transition(record, "recovered-push", "pushed", [readback.receipt]);
342
+ }
343
+ if (readback.snapshot.branch_head_oid !== null) {
344
+ return block(record, "recovered push points at a different remote commit", readback.receipt);
345
+ }
346
+ return block(record, "push contact is ambiguous and remote readback found no branch", readback.receipt);
347
+ }
348
+ function advancePullRequest(record) {
349
+ const readback = readRemote(record, "before-pr");
350
+ if (readback.snapshot.branch_head_oid !== record.payload.subject.expected_commit) {
351
+ return { record: block(record, "verified review branch disappeared or changed before PR creation", readback.receipt), waiting: false };
352
+ }
353
+ if (readback.snapshot.pull_request) {
354
+ return { record: deliveries.transition(record, "existing-draft-pr", "awaiting-check", [readback.receipt], (payload) => ({
355
+ ...payload,
356
+ pull_request: readback.snapshot.pull_request,
357
+ })), waiting: false };
358
+ }
359
+ const request = createPullRequestRequest(record, readback.snapshot.repository_node_id, env);
360
+ const contact = disclose(record, "github-pr-create", "github-api-via-gh", "create-pr", request);
361
+ const claimed = deliveries.claim(record, "pr-create-intent", "pr-pending", [
362
+ readback.receipt,
363
+ { phase: "create-pr", operation: "github-pr-create", contact, response_sha256: null },
364
+ ]);
365
+ if (claimed.status === "replayed")
366
+ return { record: claimed.record, waiting: true };
367
+ let pending = claimed.record;
368
+ const result = safeRun(runner, request);
369
+ const responseSha256 = processResultDigest(result);
370
+ pending = deliveries.transition(pending, `pr-result-${responseSha256.slice(0, 12)}`, "pr-pending", [], (payload) => ({
371
+ ...payload,
372
+ contacts: replacePendingReceipt(payload.contacts, contact, responseSha256),
373
+ }));
374
+ if (processSucceeded(result))
375
+ parseCreatedPullRequest(result, pending);
376
+ const recovered = recoverPullRequest(pending);
377
+ return { record: recovered, waiting: recovered.state === "pr-pending" };
378
+ }
379
+ function recoverPullRequest(record) {
380
+ const readback = readRemote(record, "recover-pr");
381
+ if (readback.snapshot.branch_head_oid !== record.payload.subject.expected_commit) {
382
+ return block(record, "review branch changed while recovering draft PR", readback.receipt);
383
+ }
384
+ if (!readback.snapshot.pull_request) {
385
+ return block(record, "ambiguous draft PR creation found no matching remote PR", readback.receipt);
386
+ }
387
+ return deliveries.transition(record, "pr-readback-confirmed", "awaiting-check", [readback.receipt], (payload) => ({
388
+ ...payload,
389
+ pull_request: readback.snapshot.pull_request,
390
+ }));
391
+ }
392
+ function finishAfterTypedEvents(record, issueScanContent) {
393
+ const events = findRequiredEvents(record);
394
+ if (!events)
395
+ return undefined;
396
+ if (events.outcome === "blocked")
397
+ return block(record, events.reason);
398
+ const readback = readRemote(record, "final-review-read");
399
+ if (readback.snapshot.branch_head_oid !== record.payload.subject.expected_commit
400
+ || !readback.snapshot.pull_request
401
+ || !samePullRequest(readback.snapshot.pull_request, record.payload.pull_request))
402
+ return block(record, "final GitHub readback no longer matches the verified draft PR", readback.receipt);
403
+ const finalReadDisclosure = readLiveContact(stateRoot, readback.receipt.contact);
404
+ const issueEvent = findIssueEvent(record, issueScanContent);
405
+ const contactEvidence = requiredContactEvidence(record, issueEvent.observed_at, events, finalReadDisclosure.disclosed_at);
406
+ const finalizedAt = timestampAfter(record.payload.updated_at, issueEvent.observed_at, events.pull_request_observed_at, events.check_observed_at, finalReadDisclosure.disclosed_at, ...contactEvidence.contacts.map((contact) => contact.disclosed_at));
407
+ const proofCandidate = buildProof(record, events, readback, issueEvent, contactEvidence, finalizedAt);
408
+ const scan = secretScanner({
409
+ packageRoot,
410
+ stateRoot,
411
+ ticketId: record.payload.subject.ticket_id,
412
+ phase: "final",
413
+ worktreeRoot: record.payload.subject.worktree_root,
414
+ baseCommit: record.payload.subject.worktree_base_commit,
415
+ verifiedCommit: record.payload.subject.expected_commit,
416
+ outbound: {
417
+ delivery: outboundPlan(record.payload.repository, record.payload.issue_number, record.payload.issue_node_id, issueScanContent, record.payload.subject, record.payload.required_check),
418
+ proof: proofCandidate,
419
+ },
420
+ env,
421
+ runner,
422
+ clock: () => finalizedAt,
423
+ });
424
+ assertSecretScan(scan.result, "final", record.payload.subject);
425
+ const proof = writeProof(proofCandidate, scan.reference);
426
+ return deliveries.transition(record, "review-ready", "ready", [readback.receipt], (payload) => ({
427
+ ...payload,
428
+ final_secret_scan: scan.reference,
429
+ pull_request_event: events.pullRequest,
430
+ check_event: events.check,
431
+ proof,
432
+ }));
433
+ }
434
+ function readRemote(record, phase) {
435
+ const request = reviewReadRequest(record, env);
436
+ const contact = disclose(record, "github-review-read", "github-api-via-gh", phase, request);
437
+ const result = safeRun(runner, request);
438
+ if (!processSucceeded(result))
439
+ throw new Error("GitHub review read failed");
440
+ return {
441
+ snapshot: parseReviewRead(result, record),
442
+ receipt: {
443
+ phase,
444
+ operation: "github-review-read",
445
+ contact,
446
+ response_sha256: processResultDigest(result),
447
+ },
448
+ };
449
+ }
450
+ function disclose(record, operation, contactSurface, phase, request) {
451
+ const authorization = readLiveDeliveryAuthorization(stateRoot);
452
+ if (!authorization
453
+ || authorization.authorization_id !== record.payload.authorization_id
454
+ || authorization.conductor_session_id !== record.payload.conductor_session_id
455
+ || authorization.repository !== record.payload.repository)
456
+ throw new Error("review delivery live authorization changed");
457
+ return discloseLiveContact({
458
+ stateRoot,
459
+ authorization,
460
+ operation,
461
+ contactSurface,
462
+ subjectId: createStableId("review-contact", record.record_id, phase, String(record.version)),
463
+ request: publicProcessRequest(request),
464
+ disclosedAt: nextTimestamp(record.payload.updated_at, clock),
465
+ });
466
+ }
467
+ function findRequiredEvents(record) {
468
+ if (!record.payload.pull_request)
469
+ throw new Error("awaiting-check delivery lacks its draft PR");
470
+ let pullRequest;
471
+ let check;
472
+ const removals = [];
473
+ for (const event of runtime.outbox.listEvents()) {
474
+ if (event.event_kind !== "github-observed" || event.payload.repository !== record.payload.repository)
475
+ continue;
476
+ if (typeof event.payload.payload_sha256 !== "string")
477
+ continue;
478
+ const digest = event.payload.payload_sha256;
479
+ const fileName = event.payload.snapshot_file;
480
+ if (!SHA256_PATTERN.test(digest) || fileName !== `${digest}.json`)
481
+ continue;
482
+ const snapshot = readJsonFileNoFollow(join(stateRoot, "github-snapshots", fileName), MAX_GITHUB_BYTES);
483
+ if (sha256(canonicalJson(snapshot)) !== digest || !isRecord(snapshot))
484
+ continue;
485
+ const baseCandidate = {
486
+ reference: { event_id: event.event_id, payload_sha256: digest },
487
+ observationKey: String(event.payload.observation_key),
488
+ observedAt: canonicalTimestamp(event.occurred_at, "review GitHub event timestamp"),
489
+ eventId: event.event_id,
490
+ change: event.payload.change,
491
+ snapshot,
492
+ ambiguous: false,
493
+ };
494
+ if (event.payload.change === "removed") {
495
+ const candidate = { ...baseCandidate, effectiveAt: baseCandidate.observedAt };
496
+ removals.push(candidate);
497
+ continue;
498
+ }
499
+ if (event.payload.observation_kind === "pull_request"
500
+ && snapshot.number === record.payload.pull_request.number
501
+ && snapshot.head_ref_name === record.payload.review_branch
502
+ && snapshot.base_ref_name === BASE_BRANCH) {
503
+ const candidate = {
504
+ ...baseCandidate,
505
+ effectiveAt: eventEffectiveTimestamp(snapshot.updated_at, baseCandidate.observedAt),
506
+ };
507
+ pullRequest = selectLatestCandidate(pullRequest, candidate);
508
+ }
509
+ if (event.payload.observation_kind === "check"
510
+ && snapshot.pull_request_number === record.payload.pull_request.number
511
+ && snapshot.commit_oid === record.payload.subject.expected_commit
512
+ && snapshot.name === record.payload.required_check) {
513
+ const candidate = {
514
+ ...baseCandidate,
515
+ effectiveAt: eventEffectiveTimestamp(snapshot.updated_at ?? snapshot.completed_at ?? snapshot.started_at, baseCandidate.observedAt),
516
+ };
517
+ check = selectLatestCandidate(check, candidate);
518
+ }
519
+ }
520
+ for (const removal of removals) {
521
+ if (pullRequest?.observationKey === removal.observationKey) {
522
+ pullRequest = selectLatestCandidate(pullRequest, removal);
523
+ }
524
+ if (check?.observationKey === removal.observationKey)
525
+ check = selectLatestCandidate(check, removal);
526
+ }
527
+ if (!pullRequest || !check)
528
+ return undefined;
529
+ if (pullRequest.ambiguous || check.ambiguous) {
530
+ return { outcome: "blocked", reason: "required GitHub review evidence is chronologically ambiguous" };
531
+ }
532
+ if (pullRequest.change !== "present"
533
+ || pullRequest.snapshot.state !== "OPEN"
534
+ || pullRequest.snapshot.is_draft !== true
535
+ || pullRequest.snapshot.head_oid !== record.payload.subject.expected_commit)
536
+ return { outcome: "blocked", reason: "required draft PR event is no longer current" };
537
+ const checkPassed = (check.snapshot.status === "COMPLETED" && check.snapshot.conclusion === "SUCCESS")
538
+ || (check.snapshot.check_type === "status_context" && check.snapshot.status === "SUCCESS");
539
+ if (!checkPassed) {
540
+ const checkTerminal = check.change !== "present"
541
+ || check.snapshot.status === "COMPLETED"
542
+ || (check.snapshot.check_type === "status_context"
543
+ && ["ERROR", "FAILURE"].includes(String(check.snapshot.status)));
544
+ return checkTerminal
545
+ ? { outcome: "blocked", reason: "latest required GitHub check did not pass" }
546
+ : undefined;
547
+ }
548
+ return {
549
+ outcome: "ready",
550
+ pullRequest: pullRequest.reference,
551
+ check: check.reference,
552
+ pull_request_observed_at: pullRequest.observedAt,
553
+ check_observed_at: check.observedAt,
554
+ };
555
+ }
556
+ function assertReadyAttachments(record) {
557
+ const finalScan = record.payload.final_secret_scan;
558
+ const proofRef = record.payload.proof;
559
+ if (!finalScan || !proofRef || !record.payload.pull_request_event || !record.payload.check_event) {
560
+ throw new Error("ready review delivery is missing its evidence attachments");
561
+ }
562
+ validateStoredScan(record.payload.preflight_secret_scan, record.payload, stateRoot, "preflight");
563
+ const finalScanResult = validateStoredScan(finalScan, record.payload, stateRoot, "final");
564
+ const currentEvents = findRequiredEvents(record);
565
+ if (!currentEvents
566
+ || currentEvents.outcome !== "ready"
567
+ || canonicalJson(currentEvents.pullRequest) !== canonicalJson(record.payload.pull_request_event)
568
+ || canonicalJson(currentEvents.check) !== canonicalJson(record.payload.check_event))
569
+ throw new Error("ready review delivery typed-event evidence is missing or stale");
570
+ const proof = readJsonFileNoFollow(join(stateRoot, "review-proofs", proofRef.file_name), MAX_PROOF_BYTES);
571
+ if (!isRecord(proof) || !isRecord(proof.contact_journal) || !Array.isArray(proof.contact_journal.contacts)) {
572
+ throw new Error("ready review proof lacks its contact-journal snapshot");
573
+ }
574
+ const ticket = runtime.tickets.lifecycle.read(record.payload.subject.ticket_id);
575
+ const issueContent = runtime.tickets.materializer.readContent({
576
+ ticket_id: ticket.record_id,
577
+ sha256: ticket.payload.content_sha256,
578
+ file_name: ticket.payload.content_file,
579
+ });
580
+ const issueEvent = findIssueEvent(record, {
581
+ title: issueContent.title,
582
+ body: issueContent.body,
583
+ labels: issueContent.labels,
584
+ url: issueContent.url ?? "",
585
+ });
586
+ if (!isRecord(proof.issue) || canonicalJson(proof.issue.event) !== canonicalJson(issueEvent.reference)) {
587
+ throw new Error("ready review proof issue event is missing or stale");
588
+ }
589
+ if (proof.contact_journal.authorization_id !== record.payload.authorization_id) {
590
+ throw new Error("ready review proof contact authorization changed");
591
+ }
592
+ const finalReadReceipts = record.payload.contacts.filter((receipt) => receipt.phase === "final-review-read");
593
+ if (finalReadReceipts.length !== 1)
594
+ throw new Error("ready review proof lacks its final GitHub read contact");
595
+ const finalReadDisclosure = readLiveContact(stateRoot, finalReadReceipts[0].contact);
596
+ const contactEvidence = requiredContactEvidence(record, issueEvent.observed_at, currentEvents, finalReadDisclosure.disclosed_at);
597
+ if (proof.contact_journal.cutoff_at !== contactEvidence.cutoffAt
598
+ || canonicalJson(proof.contact_journal.sync_event_times) !== canonicalJson(contactEvidence.syncEventTimes)
599
+ || canonicalJson(proof.contact_journal.contacts) !== canonicalJson(contactEvidence.contacts))
600
+ throw new Error("ready review proof contact evidence changed");
601
+ const proofCreatedAt = canonicalTimestamp(String(proof.created_at), "review proof timestamp");
602
+ const latestBoundTimestamp = Math.max(Date.parse(issueEvent.observed_at), Date.parse(currentEvents.pull_request_observed_at), Date.parse(currentEvents.check_observed_at), Date.parse(finalReadDisclosure.disclosed_at), ...contactEvidence.contacts.map((contact) => Date.parse(contact.disclosed_at)));
603
+ if (proofCreatedAt !== finalScanResult.created_at
604
+ || Date.parse(proofCreatedAt) <= latestBoundTimestamp)
605
+ throw new Error("ready review proof chronology changed");
606
+ }
607
+ function findIssueEvent(record, issue) {
608
+ let selected;
609
+ for (const event of runtime.outbox.listEvents()) {
610
+ if (event.event_kind !== "github-observed"
611
+ || event.payload.repository !== record.payload.repository
612
+ || event.payload.observation_kind !== "issue"
613
+ || event.payload.change !== "present"
614
+ || typeof event.payload.payload_sha256 !== "string")
615
+ continue;
616
+ const digest = event.payload.payload_sha256;
617
+ const fileName = event.payload.snapshot_file;
618
+ if (!SHA256_PATTERN.test(digest) || fileName !== `${digest}.json`)
619
+ continue;
620
+ const snapshot = readJsonFileNoFollow(join(stateRoot, "github-snapshots", fileName), MAX_GITHUB_BYTES);
621
+ if (sha256(canonicalJson(snapshot)) !== digest
622
+ || !isRecord(snapshot)
623
+ || snapshot.issue_node_id !== record.payload.issue_node_id
624
+ || snapshot.title !== issue.title
625
+ || snapshot.body !== issue.body
626
+ || canonicalJson(snapshot.labels) !== canonicalJson(issue.labels)
627
+ || snapshot.url !== issue.url)
628
+ continue;
629
+ const candidate = {
630
+ reference: { event_id: event.event_id, payload_sha256: digest },
631
+ observed_at: canonicalTimestamp(event.occurred_at, "review issue event timestamp"),
632
+ effective_at: eventEffectiveTimestamp(snapshot.updated_at, event.occurred_at),
633
+ event_id: event.event_id,
634
+ };
635
+ if (!selected
636
+ || Date.parse(candidate.effective_at) > Date.parse(selected.effective_at)
637
+ || (candidate.effective_at === selected.effective_at && candidate.event_id.localeCompare(selected.event_id) > 0))
638
+ selected = candidate;
639
+ }
640
+ if (!selected)
641
+ throw new Error("review proof lacks the typed issue event for its canonical ticket content");
642
+ return { reference: selected.reference, observed_at: selected.observed_at };
643
+ }
644
+ function buildProof(record, events, readback, issueEvent, contactEvidence, finalizedAt) {
645
+ const pullRequest = readback.snapshot.pull_request;
646
+ const finalReadReceipt = {
647
+ ...readback.receipt,
648
+ response_sha256: sha256(canonicalJson(readback.snapshot)),
649
+ };
650
+ return {
651
+ schema_version: 1,
652
+ proof_id: createStableId("review-proof", record.record_id, record.payload.subject.expected_commit),
653
+ delivery_id: record.record_id,
654
+ repository: record.payload.repository,
655
+ issue: {
656
+ number: record.payload.issue_number,
657
+ node_id: record.payload.issue_node_id,
658
+ url_sha256: record.payload.issue_url_sha256,
659
+ ticket_id: record.payload.subject.ticket_id,
660
+ source_ref_sha256: record.payload.source_ref_sha256,
661
+ event: issueEvent.reference,
662
+ },
663
+ goal: { id: record.payload.subject.goal_id, version: record.payload.subject.goal_version },
664
+ dispatch: { id: record.payload.subject.dispatch_id, version: record.payload.subject.dispatch_version },
665
+ session: { id: record.payload.subject.session_id },
666
+ worker: { record_id: record.payload.subject.worker_record_id, id: record.payload.subject.worker_id },
667
+ worktree: {
668
+ id: record.payload.subject.worktree_id,
669
+ root_sha256: sha256(record.payload.subject.worktree_root),
670
+ branch: record.payload.review_branch,
671
+ base_commit: record.payload.subject.worktree_base_commit,
672
+ verified_commit: record.payload.subject.expected_commit,
673
+ },
674
+ checks: {
675
+ deterministic_evidence: record.payload.subject.deterministic_evidence,
676
+ grade_evidence: record.payload.subject.grade_evidence,
677
+ required_remote_check: { name: record.payload.required_check, event: events.check },
678
+ },
679
+ github: {
680
+ base_branch: BASE_BRANCH,
681
+ branch: record.payload.review_branch,
682
+ pull_request: { ...pullRequest, event: events.pullRequest },
683
+ contacts: [...record.payload.contacts, finalReadReceipt],
684
+ },
685
+ contact_journal: {
686
+ authorization_id: record.payload.authorization_id,
687
+ cutoff_at: contactEvidence.cutoffAt,
688
+ sync_event_times: contactEvidence.syncEventTimes,
689
+ contacts: contactEvidence.contacts,
690
+ },
691
+ secret_scans: { preflight: record.payload.preflight_secret_scan },
692
+ release: { status: "not_stable", merge_disposition: "human_hold", automatic_merge: false },
693
+ created_at: finalizedAt,
694
+ };
695
+ }
696
+ function requiredContactEvidence(record, issueObservedAt, events, cutoffAt) {
697
+ const gradeRequestId = gates.read(record.payload.result_gate_id).payload.grade_request?.request_id;
698
+ if (!gradeRequestId)
699
+ throw new Error("review proof lacks its opposite-engine grade request identity");
700
+ const syncEventTimes = [...new Set([
701
+ issueObservedAt,
702
+ events.pull_request_observed_at,
703
+ events.check_observed_at,
704
+ ])].sort();
705
+ const canonicalCutoff = canonicalTimestamp(cutoffAt, "review contact evidence cutoff");
706
+ const providerBindings = [
707
+ { operation: "conductor-session", subjectId: record.payload.conductor_session_id },
708
+ { operation: "worker-session", subjectId: record.payload.subject.session_id },
709
+ { operation: "opposite-engine-grade", subjectId: gradeRequestId },
710
+ ];
711
+ const selected = listLiveContacts(stateRoot)
712
+ .filter(({ disclosure }) => (disclosure.authorization_id === record.payload.authorization_id
713
+ && disclosure.repository === record.payload.repository
714
+ && Date.parse(disclosure.disclosed_at) <= Date.parse(canonicalCutoff)
715
+ && (providerBindings.some((binding) => (disclosure.operation === binding.operation
716
+ && disclosure.subject_id === binding.subjectId))
717
+ || (disclosure.operation === "github-event-sync"
718
+ && syncEventTimes.includes(disclosure.disclosed_at)))));
719
+ for (const binding of providerBindings) {
720
+ const matching = selected.filter(({ disclosure }) => (disclosure.operation === binding.operation
721
+ && disclosure.subject_id === binding.subjectId));
722
+ if (matching.length === 0) {
723
+ throw new Error(`review contact journal lacks ${binding.operation} evidence`);
724
+ }
725
+ }
726
+ for (const eventTime of syncEventTimes) {
727
+ if (!selected.some(({ disclosure }) => (disclosure.operation === "github-event-sync"
728
+ && disclosure.disclosed_at === eventTime)))
729
+ throw new Error(`review contact journal lacks github-event-sync evidence for ${eventTime}`);
730
+ }
731
+ const contacts = selected
732
+ .map(({ reference, disclosure }) => ({
733
+ reference,
734
+ operation: disclosure.operation,
735
+ subject_id: disclosure.subject_id,
736
+ disclosed_at: disclosure.disclosed_at,
737
+ }))
738
+ .sort((left, right) => left.reference.contact_id.localeCompare(right.reference.contact_id));
739
+ return Object.freeze({
740
+ cutoffAt: canonicalCutoff,
741
+ syncEventTimes: Object.freeze(syncEventTimes),
742
+ contacts: Object.freeze(contacts),
743
+ });
744
+ }
745
+ function writeProof(candidate, finalScan) {
746
+ const proof = {
747
+ ...candidate,
748
+ secret_scans: { ...candidate.secret_scans, final: finalScan },
749
+ };
750
+ const bytes = canonicalBytes(proof);
751
+ if (bytes.byteLength > MAX_PROOF_BYTES)
752
+ throw new Error("review proof exceeds its size limit");
753
+ const proofId = String(proof.proof_id);
754
+ const root = openStableDirectory(join(stateRoot, "review-proofs"), "review proof root");
755
+ try {
756
+ const fileName = `${proofId}.json`;
757
+ const path = join(root.path, fileName);
758
+ assertFileInside(root, path);
759
+ writeImmutableFile(path, bytes);
760
+ const stored = readImmutableFile(path, MAX_PROOF_BYTES);
761
+ if (!stored.equals(bytes))
762
+ throw new Error("review proof replay changed content");
763
+ return Object.freeze({ proof_id: proofId, sha256: sha256(stored), file_name: fileName });
764
+ }
765
+ finally {
766
+ root.close();
767
+ }
768
+ }
769
+ function block(record, reason, receipt) {
770
+ return deliveries.transition(record, `blocked-${sha256(reason).slice(0, 12)}`, "blocked", receipt ? [receipt] : [], (payload) => ({
771
+ ...payload,
772
+ blocked_reason: reason,
773
+ }));
774
+ }
775
+ return Object.freeze({ advance });
776
+ }
777
+ function createReviewDeliveryStore(options) {
778
+ const payloadSchema = loadJsonSchema(join(options.packageRoot, "schemas", "review-delivery-payload.schema.json"));
779
+ const actor = assertActorId("oms-system");
780
+ const store = createRecordStore({
781
+ recordsRoot: join(options.stateRoot, "review-deliveries"),
782
+ locksRoot: join(options.stateRoot, "locks"),
783
+ recordValidator(raw) {
784
+ if (raw.record_kind !== "review-delivery" || !Object.hasOwn(LEGAL_EDGES, raw.state)) {
785
+ throw new Error("invalid review-delivery runtime record");
786
+ }
787
+ assertSchema(raw.payload, payloadSchema, "review-delivery payload");
788
+ validateReviewRecord(raw, options.stateRoot);
789
+ },
790
+ createValidator(raw) {
791
+ if (raw.version !== 0 || raw.state !== "planned" || raw.actor !== actor) {
792
+ throw new Error("review delivery must begin planned under OMS system authority");
793
+ }
794
+ },
795
+ transitionValidator(current, next) {
796
+ const before = current;
797
+ const after = next;
798
+ if (!LEGAL_EDGES[before.state].includes(after.state)) {
799
+ throw new Error(`illegal review-delivery transition: ${before.state} -> ${after.state}`);
800
+ }
801
+ for (const field of [
802
+ "delivery_id", "repository", "issue_number", "issue_node_id", "issue_url_sha256", "source_ref_sha256",
803
+ "subject", "result_gate_id", "authorization_id", "conductor_session_id", "base_branch", "review_branch",
804
+ "required_check", "created_at", "preflight_secret_scan",
805
+ ]) {
806
+ if (canonicalJson(before.payload[field]) !== canonicalJson(after.payload[field])) {
807
+ throw new Error(`review-delivery ${field} is immutable`);
808
+ }
809
+ }
810
+ if (Date.parse(after.payload.updated_at) <= Date.parse(before.payload.updated_at)) {
811
+ throw new Error("review-delivery update time must advance");
812
+ }
813
+ if (after.payload.contacts.length < before.payload.contacts.length)
814
+ throw new Error("review contacts cannot be removed");
815
+ for (let index = 0; index < before.payload.contacts.length; index += 1) {
816
+ const oldReceipt = before.payload.contacts[index];
817
+ const newReceipt = after.payload.contacts[index];
818
+ const pendingCompletion = oldReceipt.response_sha256 === null
819
+ && newReceipt.response_sha256 !== null
820
+ && canonicalJson({ ...oldReceipt, response_sha256: null }) === canonicalJson({ ...newReceipt, response_sha256: null });
821
+ if (!pendingCompletion && canonicalJson(oldReceipt) !== canonicalJson(newReceipt)) {
822
+ throw new Error("review contact receipts are append-only");
823
+ }
824
+ }
825
+ monotonicObject("pull request", before.payload.pull_request, after.payload.pull_request);
826
+ monotonicObject("pull request event", before.payload.pull_request_event, after.payload.pull_request_event);
827
+ monotonicObject("check event", before.payload.check_event, after.payload.check_event);
828
+ monotonicObject("final secret scan", before.payload.final_secret_scan, after.payload.final_secret_scan);
829
+ monotonicObject("proof", before.payload.proof, after.payload.proof);
830
+ },
831
+ });
832
+ function applyTransition(record, step, toState, receipts, update) {
833
+ return store.transition({
834
+ recordId: record.record_id,
835
+ ownerId: actor,
836
+ expectedVersion: record.version,
837
+ nonce: createStableId("review-step", record.record_id, step),
838
+ expectedActor: record.actor,
839
+ expectedAttempt: record.attempt,
840
+ expectedLeaseToken: record.lease_token,
841
+ fromStates: [record.state],
842
+ toState,
843
+ updatePayload: (current) => {
844
+ const changed = update(current.payload);
845
+ return {
846
+ ...changed,
847
+ updated_at: nextTimestamp(current.payload.updated_at, options.clock),
848
+ contacts: [...changed.contacts, ...receipts],
849
+ };
850
+ },
851
+ });
852
+ }
853
+ return {
854
+ create(input) {
855
+ const id = assertRecordId(input.deliveryId, "review delivery id");
856
+ const record = {
857
+ schema_version: 1,
858
+ record_id: id,
859
+ record_kind: assertOpaqueId("review-delivery"),
860
+ version: 0,
861
+ state: "planned",
862
+ attempt: 1,
863
+ max_attempts: 1,
864
+ actor,
865
+ lease_token: null,
866
+ transition_nonce: assertTransitionNonce(createStableId("review-create", id)),
867
+ payload: input.payload,
868
+ transition_outcomes: [],
869
+ };
870
+ record.transition_outcomes.push({
871
+ nonce: record.transition_nonce,
872
+ version: 0,
873
+ state: record.state,
874
+ attempt: 1,
875
+ actor,
876
+ lease_token: null,
877
+ });
878
+ return store.create(record);
879
+ },
880
+ readIfExists(deliveryId) {
881
+ return store.readIfExists(deliveryId);
882
+ },
883
+ transition(record, step, toState, receipts = [], update = (payload) => payload) {
884
+ return applyTransition(record, step, toState, receipts, update).record;
885
+ },
886
+ claim(record, step, toState, receipts) {
887
+ const result = applyTransition(record, step, toState, receipts, (payload) => payload);
888
+ return { status: result.status, record: result.record };
889
+ },
890
+ };
891
+ }
892
+ function validateReviewRecord(record, stateRoot) {
893
+ const payload = record.payload;
894
+ if (payload.delivery_id !== record.record_id
895
+ || record.actor !== "oms-system"
896
+ || record.lease_token !== null
897
+ || record.attempt !== 1
898
+ || record.max_attempts !== 1
899
+ || payload.base_branch !== BASE_BRANCH
900
+ || !BRANCH_PATTERN.test(payload.review_branch)
901
+ || payload.review_branch !== payload.subject.worktree_branch
902
+ || payload.subject.dispatch_id === payload.subject.ticket_id
903
+ || payload.subject.deterministic_evidence === null
904
+ || payload.subject.grade_evidence === null)
905
+ throw new Error("review-delivery identity mismatch");
906
+ assertOpaqueId(payload.result_gate_id, "review result-gate id");
907
+ assertOpaqueId(payload.authorization_id, "review authorization id");
908
+ validateRequiredCheck(payload.required_check);
909
+ canonicalTimestamp(payload.created_at, "review creation timestamp");
910
+ canonicalTimestamp(payload.updated_at, "review update timestamp");
911
+ if (Date.parse(payload.updated_at) < Date.parse(payload.created_at))
912
+ throw new Error("review update predates creation");
913
+ validateReviewSubject(payload.subject);
914
+ validateStoredScan(payload.preflight_secret_scan, payload, stateRoot, "preflight");
915
+ if (payload.final_secret_scan)
916
+ validateStoredScan(payload.final_secret_scan, payload, stateRoot, "final");
917
+ for (const receipt of payload.contacts)
918
+ validateContactReceipt(receipt, payload, stateRoot);
919
+ if (payload.pull_request)
920
+ validatePullRequest(payload.pull_request);
921
+ if (payload.pull_request_event)
922
+ validateEventReference(payload.pull_request_event);
923
+ if (payload.check_event)
924
+ validateEventReference(payload.check_event);
925
+ if (payload.proof)
926
+ validateProofReference(payload.proof, stateRoot);
927
+ if (record.state === "ready" && (payload.proof === null
928
+ || payload.final_secret_scan === null
929
+ || payload.pull_request === null
930
+ || payload.pull_request_event === null
931
+ || payload.check_event === null
932
+ || payload.blocked_reason !== null))
933
+ throw new Error("ready review delivery lacks final proof evidence");
934
+ if (record.state === "blocked" && payload.blocked_reason === null) {
935
+ throw new Error("blocked review delivery lacks a reason");
936
+ }
937
+ }
938
+ function validateReviewSubject(subject) {
939
+ exactRecord(subject, [
940
+ "attempt", "deterministic_evidence", "dispatch_id", "dispatch_version", "expected_commit",
941
+ "goal_id", "goal_version", "grade_evidence", "lease_token", "session_id", "ticket_id",
942
+ "worker_id", "worker_record_id", "worktree_base_commit", "worktree_branch", "worktree_id", "worktree_root",
943
+ ], "review subject");
944
+ for (const [label, value] of [
945
+ ["goal id", subject.goal_id], ["dispatch id", subject.dispatch_id], ["ticket id", subject.ticket_id],
946
+ ["lease token", subject.lease_token], ["worker record id", subject.worker_record_id],
947
+ ["worker id", subject.worker_id], ["session id", subject.session_id], ["worktree id", subject.worktree_id],
948
+ ])
949
+ assertOpaqueId(value, `review ${label}`);
950
+ if (!Number.isSafeInteger(subject.goal_version) || subject.goal_version < 0)
951
+ throw new Error("invalid review goal version");
952
+ if (!Number.isSafeInteger(subject.dispatch_version) || subject.dispatch_version < 0)
953
+ throw new Error("invalid review dispatch version");
954
+ positiveInteger(subject.attempt, "review attempt");
955
+ if (!BRANCH_PATTERN.test(subject.worktree_branch) || !subject.worktree_root.startsWith("/"))
956
+ throw new Error("invalid review worktree");
957
+ commit(subject.worktree_base_commit, "review base commit");
958
+ commit(subject.expected_commit, "review verified commit");
959
+ validateEvidenceReference(subject.deterministic_evidence);
960
+ validateEvidenceReference(subject.grade_evidence);
961
+ }
962
+ function validateEvidenceReference(reference) {
963
+ exactRecord(reference, ["evidence_id", "file_name", "sha256"], "review evidence reference");
964
+ assertOpaqueId(reference.evidence_id, "review evidence id");
965
+ if (!SHA256_PATTERN.test(reference.sha256) || reference.file_name !== `${reference.evidence_id}.json`) {
966
+ throw new Error("invalid review evidence reference");
967
+ }
968
+ }
969
+ function validateScanReference(reference) {
970
+ exactRecord(reference, ["file_name", "scan_id", "sha256"], "review secret scan reference");
971
+ assertOpaqueId(reference.scan_id, "review secret scan id");
972
+ if (!SHA256_PATTERN.test(reference.sha256) || reference.file_name !== `${reference.scan_id}.json`) {
973
+ throw new Error("invalid review secret scan reference");
974
+ }
975
+ }
976
+ function validateStoredScan(reference, payload, stateRoot, phase) {
977
+ validateScanReference(reference);
978
+ const scan = readSecretScan(stateRoot, reference);
979
+ if (scan.phase !== phase
980
+ || scan.ticket_id !== payload.subject.ticket_id
981
+ || scan.base_commit !== payload.subject.worktree_base_commit
982
+ || scan.verified_commit !== payload.subject.expected_commit
983
+ || scan.passed !== true)
984
+ throw new Error(`review ${phase} secret scan binding is invalid`);
985
+ return scan;
986
+ }
987
+ function validateContactReceipt(receipt, payload, stateRoot) {
988
+ exactRecord(receipt, ["contact", "operation", "phase", "response_sha256"], "review contact receipt");
989
+ if (!/^[a-z0-9-]{1,64}$/u.test(receipt.phase) || !["git-branch-push", "github-review-read", "github-pr-create"].includes(receipt.operation)) {
990
+ throw new Error("invalid review contact receipt");
991
+ }
992
+ exactRecord(receipt.contact, ["contact_id", "file_name", "sha256"], "review contact reference");
993
+ assertOpaqueId(receipt.contact.contact_id, "review contact id");
994
+ if (!SHA256_PATTERN.test(receipt.contact.sha256)
995
+ || receipt.contact.file_name !== `${receipt.contact.contact_id}.json`
996
+ || (receipt.response_sha256 !== null && !SHA256_PATTERN.test(receipt.response_sha256)))
997
+ throw new Error("invalid review contact reference");
998
+ const disclosure = readLiveContact(stateRoot, receipt.contact);
999
+ if (disclosure.operation !== receipt.operation
1000
+ || disclosure.authorization_id !== payload.authorization_id
1001
+ || disclosure.conductor_session_id !== payload.conductor_session_id
1002
+ || disclosure.repository !== payload.repository)
1003
+ throw new Error("review contact disclosure does not match its delivery authority");
1004
+ }
1005
+ function validatePullRequest(pullRequest) {
1006
+ exactRecord(pullRequest, ["head_oid", "node_id", "number", "url_sha256"], "review pull request reference");
1007
+ nodeId(pullRequest.node_id, "review PR node id");
1008
+ positiveInteger(pullRequest.number, "review PR number");
1009
+ commit(pullRequest.head_oid, "review PR head");
1010
+ if (!SHA256_PATTERN.test(pullRequest.url_sha256))
1011
+ throw new Error("invalid review PR URL digest");
1012
+ }
1013
+ function validateEventReference(reference) {
1014
+ exactRecord(reference, ["event_id", "payload_sha256"], "review event reference");
1015
+ assertOpaqueId(reference.event_id, "review event id");
1016
+ if (!SHA256_PATTERN.test(reference.payload_sha256))
1017
+ throw new Error("invalid review event digest");
1018
+ }
1019
+ function validateProofReference(reference, stateRoot) {
1020
+ exactRecord(reference, ["file_name", "proof_id", "sha256"], "review proof reference");
1021
+ assertOpaqueId(reference.proof_id, "review proof id");
1022
+ if (!SHA256_PATTERN.test(reference.sha256) || reference.file_name !== `${reference.proof_id}.json`) {
1023
+ throw new Error("invalid review proof reference");
1024
+ }
1025
+ const path = join(stateRoot, "review-proofs", reference.file_name);
1026
+ if (sha256(readImmutableFile(path, MAX_PROOF_BYTES)) !== reference.sha256)
1027
+ throw new Error("review proof digest mismatch");
1028
+ }
1029
+ function assertReplayBinding(record, canonical) {
1030
+ if (canonicalJson(record.payload.subject) !== canonicalJson(canonical.subject)
1031
+ || record.payload.repository !== canonical.repository
1032
+ || record.payload.issue_number !== canonical.issueNumber
1033
+ || record.payload.issue_node_id !== canonical.issueNodeId
1034
+ || record.payload.issue_url_sha256 !== canonical.issueUrlSha256
1035
+ || record.payload.source_ref_sha256 !== canonical.sourceRefSha256
1036
+ || record.payload.authorization_id !== canonical.authorizationId
1037
+ || record.payload.conductor_session_id !== canonical.conductorSessionId)
1038
+ throw new Error("review delivery replay changed its protected authority binding");
1039
+ }
1040
+ function reviewReadRequest(record, env) {
1041
+ const target = normalizeGithubSyncTarget(record.payload.repository);
1042
+ return githubRequest(record.payload.subject.worktree_root, {
1043
+ query: REVIEW_QUERY,
1044
+ variables: { owner: target.owner, name: target.name, branchRef: `refs/heads/${record.payload.review_branch}` },
1045
+ }, env);
1046
+ }
1047
+ function createPullRequestRequest(record, repositoryId, env) {
1048
+ return githubRequest(record.payload.subject.worktree_root, {
1049
+ query: CREATE_PR_MUTATION,
1050
+ variables: {
1051
+ repositoryId,
1052
+ baseRefName: BASE_BRANCH,
1053
+ headRefName: record.payload.review_branch,
1054
+ title: draftPullRequestTitle(record.payload.issue_number),
1055
+ body: draftPullRequestBody(record.payload.issue_number, record.payload.subject),
1056
+ },
1057
+ }, env);
1058
+ }
1059
+ function pushRequest(record, env) {
1060
+ const sshTarget = `git@github.com:${record.payload.repository}.git`;
1061
+ return {
1062
+ command: "git",
1063
+ args: [
1064
+ "push",
1065
+ "--porcelain",
1066
+ "--no-verify",
1067
+ sshTarget,
1068
+ `${record.payload.subject.expected_commit}:refs/heads/${record.payload.review_branch}`,
1069
+ ],
1070
+ cwd: record.payload.subject.worktree_root,
1071
+ env: gitEnvironment(env),
1072
+ timeoutMs: 60_000,
1073
+ maxBufferBytes: 512 * 1024,
1074
+ };
1075
+ }
1076
+ function githubRequest(cwd, input, env) {
1077
+ return {
1078
+ command: "gh",
1079
+ args: ["api", "graphql", "--input", "-"],
1080
+ cwd,
1081
+ env: githubEnvironment(env),
1082
+ input: JSON.stringify(input),
1083
+ timeoutMs: 30_000,
1084
+ maxBufferBytes: MAX_GITHUB_BYTES,
1085
+ };
1086
+ }
1087
+ function parseReviewRead(result, record) {
1088
+ const root = parseGraphql(result.stdout, "review read");
1089
+ const data = exactRecord(root.data, ["repository", "rateLimit"], "review read data");
1090
+ assertRateLimit(data.rateLimit);
1091
+ const repository = exactRecord(data.repository, ["id", "pullRequests", "ref"], "review repository");
1092
+ const repositoryNodeId = nodeId(repository.id, "review repository id");
1093
+ let branchHead = null;
1094
+ if (repository.ref !== null) {
1095
+ const ref = exactRecord(repository.ref, ["target"], "review ref");
1096
+ const target = exactRecord(ref.target, ["oid"], "review ref target");
1097
+ branchHead = commit(target.oid, "review branch head");
1098
+ }
1099
+ const pullRequestsConnection = exactRecord(repository.pullRequests, ["nodes"], "review pull requests");
1100
+ if (!Array.isArray(pullRequestsConnection.nodes))
1101
+ throw new Error("review pull request nodes are invalid");
1102
+ let pullRequest = null;
1103
+ for (const value of pullRequestsConnection.nodes) {
1104
+ const candidate = parsePullRequest(value, record);
1105
+ if (!candidate)
1106
+ continue;
1107
+ if (pullRequest)
1108
+ throw new Error("multiple open draft PRs match the OMS review branch");
1109
+ pullRequest = candidate;
1110
+ }
1111
+ return {
1112
+ repository_node_id: repositoryNodeId,
1113
+ branch_head_oid: branchHead,
1114
+ pull_request: pullRequest,
1115
+ };
1116
+ }
1117
+ function parseCreatedPullRequest(result, record) {
1118
+ const root = parseGraphql(result.stdout, "draft PR create");
1119
+ const data = exactRecord(root.data, ["createPullRequest"], "draft PR data");
1120
+ const created = exactRecord(data.createPullRequest, ["pullRequest"], "draft PR mutation");
1121
+ const candidate = parsePullRequest(created.pullRequest, record);
1122
+ if (!candidate)
1123
+ throw new Error("created PR does not match the protected review delivery");
1124
+ return candidate;
1125
+ }
1126
+ function parsePullRequest(value, record) {
1127
+ const pr = exactRecord(value, [
1128
+ "baseRefName", "commits", "headRefName", "id", "isDraft", "number", "state", "url",
1129
+ ], "review pull request");
1130
+ if (pr.headRefName !== record.payload.review_branch || pr.baseRefName !== BASE_BRANCH || pr.state !== "OPEN")
1131
+ return null;
1132
+ if (pr.isDraft !== true)
1133
+ throw new Error("matching review PR must remain draft");
1134
+ const commits = exactRecord(pr.commits, ["nodes", "totalCount"], "review PR commits");
1135
+ if (!Number.isSafeInteger(commits.totalCount) || Number(commits.totalCount) < 1 || !Array.isArray(commits.nodes) || commits.nodes.length !== 1) {
1136
+ throw new Error("review PR head commit is ambiguous");
1137
+ }
1138
+ const node = exactRecord(commits.nodes[0], ["commit"], "review PR commit node");
1139
+ const commitNode = exactRecord(node.commit, ["oid"], "review PR commit");
1140
+ const headOid = commit(commitNode.oid, "review PR head");
1141
+ if (headOid !== record.payload.subject.expected_commit)
1142
+ throw new Error("review PR head differs from the verified commit");
1143
+ const number = positiveInteger(pr.number, "review PR number");
1144
+ const url = githubUrl(pr.url, "review PR URL");
1145
+ return {
1146
+ node_id: nodeId(pr.id, "review PR id"),
1147
+ number,
1148
+ url_sha256: sha256(url),
1149
+ head_oid: headOid,
1150
+ };
1151
+ }
1152
+ function parseGraphql(stdout, label) {
1153
+ if (Buffer.byteLength(stdout, "utf8") > MAX_GITHUB_BYTES)
1154
+ throw new Error(`${label} exceeds its output limit`);
1155
+ let value;
1156
+ try {
1157
+ value = JSON.parse(stdout);
1158
+ }
1159
+ catch {
1160
+ throw new Error(`${label} returned malformed JSON`);
1161
+ }
1162
+ const root = exactRecord(value, ["data"], label);
1163
+ return root;
1164
+ }
1165
+ function assertRateLimit(value) {
1166
+ const rate = exactRecord(value, ["remaining", "resetAt"], "GitHub rate limit");
1167
+ if (!Number.isSafeInteger(rate.remaining) || Number(rate.remaining) < 1)
1168
+ throw new Error("GitHub rate limit is exhausted");
1169
+ canonicalTimestamp(String(rate.resetAt), "GitHub rate reset timestamp");
1170
+ }
1171
+ function outboundPlan(repository, issueNumber, issueNodeId, issueScanContent, subject, requiredCheck) {
1172
+ const target = normalizeGithubSyncTarget(repository);
1173
+ const branch = subject.worktree_branch;
1174
+ const title = draftPullRequestTitle(issueNumber);
1175
+ const body = draftPullRequestBody(issueNumber, subject);
1176
+ return {
1177
+ repository,
1178
+ issue: {
1179
+ number: issueNumber,
1180
+ node_id: issueNodeId,
1181
+ title: issueScanContent.title,
1182
+ body: issueScanContent.body,
1183
+ labels: issueScanContent.labels,
1184
+ url: issueScanContent.url,
1185
+ },
1186
+ branch,
1187
+ base_branch: BASE_BRANCH,
1188
+ expected_commit: subject.expected_commit,
1189
+ required_check: requiredCheck,
1190
+ process_argv: [
1191
+ ["git", "remote", "get-url", "--push", "origin"],
1192
+ ["git", "merge-base", "--is-ancestor", subject.worktree_base_commit, subject.expected_commit],
1193
+ ["git", "push", "--porcelain", "--no-verify", `git@github.com:${repository}.git`, `${subject.expected_commit}:refs/heads/${branch}`],
1194
+ ["gh", "api", "graphql", "--input", "-"],
1195
+ ],
1196
+ github_inputs: [
1197
+ { query: REVIEW_QUERY, variables: { owner: target.owner, name: target.name, branchRef: `refs/heads/${branch}` } },
1198
+ {
1199
+ query: CREATE_PR_MUTATION,
1200
+ variables: {
1201
+ repositoryId: "<read-back-node-id>", baseRefName: BASE_BRANCH, headRefName: branch, title, body,
1202
+ },
1203
+ },
1204
+ ],
1205
+ release: { status: "not_stable", merge_disposition: "human_hold", automatic_merge: false },
1206
+ };
1207
+ }
1208
+ function draftPullRequestTitle(issueNumber) {
1209
+ return `OMS ticket #${issueNumber}`;
1210
+ }
1211
+ function draftPullRequestBody(issueNumber, subject) {
1212
+ return [
1213
+ `Automated draft for issue #${issueNumber}.`,
1214
+ `Verified commit: ${subject.expected_commit}`,
1215
+ `Deterministic evidence: ${subject.deterministic_evidence.evidence_id} (${subject.deterministic_evidence.sha256})`,
1216
+ `Opposite-engine grade: ${subject.grade_evidence.evidence_id} (${subject.grade_evidence.sha256})`,
1217
+ "Status: READY_FOR_HUMAN after required checks are observed.",
1218
+ "Merge, ready-for-review, tag, publish, and release remain human actions.",
1219
+ ].join("\n\n");
1220
+ }
1221
+ function protectedBinding(subject) {
1222
+ return {
1223
+ goal_id: subject.goal_id,
1224
+ goal_version: subject.goal_version,
1225
+ dispatch_id: subject.dispatch_id,
1226
+ dispatch_version: subject.dispatch_version,
1227
+ ticket_id: subject.ticket_id,
1228
+ attempt: subject.attempt,
1229
+ lease_token: subject.lease_token,
1230
+ session_id: subject.session_id,
1231
+ worker_record_id: subject.worker_record_id,
1232
+ worker_id: subject.worker_id,
1233
+ worktree_id: subject.worktree_id,
1234
+ worktree_root: subject.worktree_root,
1235
+ worktree_branch: subject.worktree_branch,
1236
+ worktree_base_commit: subject.worktree_base_commit,
1237
+ expected_commit: subject.expected_commit,
1238
+ executor_role: subject.executor_role,
1239
+ executor_engine: subject.executor_engine,
1240
+ executor_evidence: subject.executor_evidence,
1241
+ };
1242
+ }
1243
+ function assertAuthorizedOrigin(repository, cwd, env, runner) {
1244
+ const result = runner({
1245
+ command: "git",
1246
+ args: ["remote", "get-url", "--push", "origin"],
1247
+ cwd,
1248
+ env: gitEnvironment(env),
1249
+ timeoutMs: 10_000,
1250
+ maxBufferBytes: 64 * 1024,
1251
+ });
1252
+ if (!processSucceeded(result))
1253
+ throw new Error("unable to verify the review push remote");
1254
+ const normalized = normalizeGithubRemoteRepository(result.stdout.trim());
1255
+ if (normalized !== repository)
1256
+ throw new Error("review push remote does not match the authorized GitHub repository");
1257
+ }
1258
+ function assertCommitAncestry(subject, env, runner) {
1259
+ const result = runner({
1260
+ command: "git",
1261
+ args: ["merge-base", "--is-ancestor", subject.worktree_base_commit, subject.expected_commit],
1262
+ cwd: subject.worktree_root,
1263
+ env: gitEnvironment(env),
1264
+ timeoutMs: 10_000,
1265
+ maxBufferBytes: 64 * 1024,
1266
+ });
1267
+ if (!processSucceeded(result))
1268
+ throw new Error("verified review commit does not descend from its frozen base");
1269
+ }
1270
+ function assertSecretScan(result, phase, subject) {
1271
+ if (result.phase !== phase
1272
+ || result.ticket_id !== subject.ticket_id
1273
+ || result.base_commit !== subject.worktree_base_commit
1274
+ || result.verified_commit !== subject.expected_commit
1275
+ || result.passed !== true)
1276
+ throw new Error(`${phase} secret scan does not match the protected review delivery`);
1277
+ }
1278
+ function toProgress(record) {
1279
+ return Object.freeze({
1280
+ status: record.state === "ready" ? "ready" : record.state === "blocked" ? "blocked" : record.state === "awaiting-check" || record.state === "pr-pending" ? "waiting" : "working",
1281
+ delivery_id: record.record_id,
1282
+ state: record.state,
1283
+ ticket_id: record.payload.subject.ticket_id,
1284
+ branch: record.payload.review_branch,
1285
+ pull_request_number: record.payload.pull_request?.number ?? null,
1286
+ proof: record.payload.proof,
1287
+ reason: record.payload.blocked_reason,
1288
+ });
1289
+ }
1290
+ function parseIssueNumber(url, repository) {
1291
+ const escaped = repository.split("/").map(escapeRegExp).join("/");
1292
+ const match = new RegExp(`^https://github\\.com/${escaped}/issues/([1-9][0-9]*)$`, "u").exec(url.toLowerCase());
1293
+ if (!match)
1294
+ throw new Error("GitHub ticket URL does not match its authorized repository");
1295
+ return positiveInteger(Number(match[1]), "GitHub issue number");
1296
+ }
1297
+ function replacePendingReceipt(receipts, contact, responseSha256) {
1298
+ let replaced = false;
1299
+ const next = receipts.map((receipt) => {
1300
+ if (receipt.contact.contact_id !== contact.contact_id || receipt.response_sha256 !== null)
1301
+ return receipt;
1302
+ replaced = true;
1303
+ return { ...receipt, response_sha256: responseSha256 };
1304
+ });
1305
+ if (!replaced)
1306
+ throw new Error("review contact result lacks its pending intent");
1307
+ return next;
1308
+ }
1309
+ function sameReference(left, right) {
1310
+ return canonicalJson(left) === canonicalJson(right);
1311
+ }
1312
+ function samePullRequest(left, right) {
1313
+ return left !== null && right !== null && canonicalJson(left) === canonicalJson(right);
1314
+ }
1315
+ function selectLatestCandidate(current, candidate) {
1316
+ if (!current)
1317
+ return candidate;
1318
+ const timeDelta = Date.parse(candidate.effectiveAt) - Date.parse(current.effectiveAt);
1319
+ if (timeDelta > 0)
1320
+ return candidate;
1321
+ if (timeDelta < 0)
1322
+ return current;
1323
+ if (candidate.change === current.change && canonicalJson(candidate.snapshot) === canonicalJson(current.snapshot)) {
1324
+ return candidate.eventId.localeCompare(current.eventId) > 0 ? candidate : current;
1325
+ }
1326
+ return { ...current, ambiguous: true };
1327
+ }
1328
+ function eventEffectiveTimestamp(value, fallback) {
1329
+ return value === null || value === undefined
1330
+ ? fallback
1331
+ : canonicalTimestamp(String(value), "review GitHub snapshot timestamp");
1332
+ }
1333
+ function monotonicObject(label, before, after) {
1334
+ if (before !== null && canonicalJson(before) !== canonicalJson(after))
1335
+ throw new Error(`${label} is immutable once attached`);
1336
+ }
1337
+ function safeRun(runner, request) {
1338
+ try {
1339
+ return runner(request);
1340
+ }
1341
+ catch (error) {
1342
+ return { status: null, signal: null, stdout: "", stderr: "", error: error instanceof Error ? error : new Error("process runner threw") };
1343
+ }
1344
+ }
1345
+ function processSucceeded(result) {
1346
+ return result.error === undefined && result.status === 0 && result.signal === null;
1347
+ }
1348
+ function processResultDigest(result) {
1349
+ return sha256(canonicalJson({
1350
+ status: result.status,
1351
+ signal: result.signal,
1352
+ stdout_sha256: sha256(result.stdout),
1353
+ stderr_sha256: sha256(result.stderr),
1354
+ error_name: result.error?.name ?? null,
1355
+ }));
1356
+ }
1357
+ function publicProcessRequest(request) {
1358
+ return {
1359
+ command: request.command,
1360
+ args: request.args,
1361
+ cwd_sha256: sha256(request.cwd),
1362
+ input_sha256: request.input === undefined ? null : sha256(request.input),
1363
+ timeout_ms: request.timeoutMs,
1364
+ max_buffer_bytes: request.maxBufferBytes,
1365
+ };
1366
+ }
1367
+ function githubEnvironment(source) {
1368
+ if (source.GH_HOST && source.GH_HOST !== "github.com")
1369
+ throw new Error("review delivery supports github.com only");
1370
+ const env = {};
1371
+ for (const key of ["PATH", "HOME", "GH_TOKEN", "GITHUB_TOKEN", "GH_HOST", "NO_COLOR"]) {
1372
+ if (source[key] !== undefined)
1373
+ env[key] = source[key];
1374
+ }
1375
+ return env;
1376
+ }
1377
+ function gitEnvironment(source) {
1378
+ const env = {};
1379
+ for (const key of ["PATH", "HOME", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL", "SYSTEMROOT", "SSH_AUTH_SOCK", "SSH_AGENT_PID"]) {
1380
+ if (source[key] !== undefined)
1381
+ env[key] = source[key];
1382
+ }
1383
+ env.GIT_TERMINAL_PROMPT = "0";
1384
+ env.GIT_CONFIG_NOSYSTEM = "1";
1385
+ env.GIT_CONFIG_GLOBAL = "/dev/null";
1386
+ return env;
1387
+ }
1388
+ function exactRecord(value, keys, label) {
1389
+ if (!isRecord(value) || Object.keys(value).sort().join("\0") !== [...keys].sort().join("\0")) {
1390
+ throw new Error(`${label} has an invalid shape`);
1391
+ }
1392
+ return value;
1393
+ }
1394
+ function nodeId(value, label) {
1395
+ if (typeof value !== "string" || !/^[A-Za-z0-9_=-]{1,256}$/u.test(value))
1396
+ throw new Error(`invalid ${label}`);
1397
+ return value;
1398
+ }
1399
+ function commit(value, label) {
1400
+ if (typeof value !== "string" || !SHA_PATTERN.test(value))
1401
+ throw new Error(`invalid ${label}`);
1402
+ return value;
1403
+ }
1404
+ function githubUrl(value, label) {
1405
+ if (typeof value !== "string" || !/^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9][0-9]*$/u.test(value)) {
1406
+ throw new Error(`invalid ${label}`);
1407
+ }
1408
+ return value;
1409
+ }
1410
+ function positiveInteger(value, label) {
1411
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
1412
+ throw new Error(`invalid ${label}`);
1413
+ return value;
1414
+ }
1415
+ function validateRequiredCheck(value) {
1416
+ if (!REQUIRED_CHECK_PATTERN.test(value))
1417
+ throw new Error("invalid required review check name");
1418
+ return value;
1419
+ }
1420
+ function nextTimestamp(previous, clock) {
1421
+ const candidate = canonicalTimestamp(clock(), "review timestamp");
1422
+ return new Date(Math.max(Date.parse(candidate), Date.parse(previous) + 1)).toISOString();
1423
+ }
1424
+ function timestampAfter(...timestamps) {
1425
+ if (timestamps.length === 0)
1426
+ throw new Error("review finalization requires evidence timestamps");
1427
+ const latest = Math.max(...timestamps.map((timestamp) => (Date.parse(canonicalTimestamp(timestamp, "review evidence timestamp")))));
1428
+ return new Date(latest + 1).toISOString();
1429
+ }
1430
+ function escapeRegExp(value) {
1431
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1432
+ }
1433
+ function canonicalBytes(value) {
1434
+ return Buffer.from(`${canonicalJson(value)}\n`, "utf8");
1435
+ }
1436
+ function canonicalJson(value) {
1437
+ if (value === undefined)
1438
+ throw new Error("review delivery data cannot be undefined");
1439
+ if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string")
1440
+ return JSON.stringify(value);
1441
+ if (Array.isArray(value))
1442
+ return `[${value.map(canonicalJson).join(",")}]`;
1443
+ if (!isRecord(value))
1444
+ throw new Error("review delivery data must be JSON");
1445
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
1446
+ }
1447
+ function sha256(value) {
1448
+ return createHash("sha256").update(value).digest("hex");
1449
+ }
1450
+ //# sourceMappingURL=review.js.map