@atolis-hq/wake 0.2.97 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (443) hide show
  1. package/README.md +5 -5
  2. package/dist/src/activities/agent/agent-activity-definition.js +26 -0
  3. package/dist/src/activities/agent/agent-activity.js +171 -0
  4. package/dist/src/activities/agent/agent-result.js +149 -0
  5. package/dist/src/activities/contracts/activity.js +10 -0
  6. package/dist/src/activities/contracts/config.js +2 -0
  7. package/dist/src/activities/contracts/event-schema.js +302 -0
  8. package/dist/src/activities/contracts/events.js +42 -0
  9. package/dist/src/activities/contracts/identifiers.js +29 -0
  10. package/dist/src/activities/contracts/registry.js +71 -0
  11. package/dist/src/activities/contracts/streams.js +17 -0
  12. package/dist/src/activities/contracts/vocabulary.js +58 -0
  13. package/dist/src/activities/index.js +21 -0
  14. package/dist/src/activities/pr/activity-support.js +111 -0
  15. package/dist/src/activities/pr/application.js +158 -0
  16. package/dist/src/activities/pr/approve.js +85 -0
  17. package/dist/src/activities/pr/capability.js +11 -0
  18. package/dist/src/activities/pr/decision-claim.js +76 -0
  19. package/dist/src/activities/pr/event-drafts.js +66 -0
  20. package/dist/src/activities/pr/intent.js +37 -0
  21. package/dist/src/activities/pr/merge-authority-gate.js +14 -0
  22. package/dist/src/activities/pr/merge.js +110 -0
  23. package/dist/src/activities/pr/policy.js +135 -0
  24. package/dist/src/activities/pr/projection.js +130 -0
  25. package/dist/src/activities/pr/vocabulary.js +30 -0
  26. package/dist/src/activities/review/authorization.js +22 -0
  27. package/dist/src/activities/review/contracts.js +22 -0
  28. package/dist/src/activities/review/signals.js +3 -0
  29. package/dist/src/bootstrap/analytics-projection.js +26 -0
  30. package/dist/src/bootstrap/board-projection.js +337 -0
  31. package/dist/src/bootstrap/composition-root.js +382 -0
  32. package/dist/src/bootstrap/config/load-config.js +51 -0
  33. package/dist/src/bootstrap/config/root-schema.js +134 -0
  34. package/dist/src/bootstrap/external-ref.js +12 -0
  35. package/dist/src/bootstrap/fake-provider-files.js +44 -0
  36. package/dist/src/bootstrap/fake-scenarios.js +22 -0
  37. package/dist/src/bootstrap/index.js +48 -0
  38. package/dist/src/bootstrap/initialise.js +436 -0
  39. package/dist/src/bootstrap/paths.js +15 -0
  40. package/dist/src/bootstrap/projection-runtime.js +27 -0
  41. package/dist/src/bootstrap/runner-quota-reporter.js +21 -0
  42. package/dist/src/bootstrap/runner-registry.js +30 -0
  43. package/dist/src/bootstrap/schedule-checkpoint-store.js +49 -0
  44. package/dist/src/bootstrap/self-update-application.js +195 -0
  45. package/dist/src/bootstrap/self-update-failure-log.js +33 -0
  46. package/dist/src/bootstrap/source-update-port.js +47 -0
  47. package/dist/src/bootstrap/status-publish-activity.js +42 -0
  48. package/dist/src/bootstrap/surface-api-applications.js +325 -0
  49. package/dist/src/bootstrap/surface-api-execution-applications.js +95 -0
  50. package/dist/src/bootstrap/surface-api-metadata.js +12 -0
  51. package/dist/src/bootstrap/surface-api-projection-pages.js +22 -0
  52. package/dist/src/bootstrap/surface-api-run-context.js +5 -0
  53. package/dist/src/bootstrap/surface-api-transcripts.js +57 -0
  54. package/dist/src/bootstrap/surface-api-work-applications.js +199 -0
  55. package/dist/src/bootstrap/surface-applications.js +7 -0
  56. package/dist/src/bootstrap/surface-cli-applications.js +701 -0
  57. package/dist/src/bootstrap/update-ledger.js +61 -0
  58. package/dist/src/bootstrap/update-maintenance-lease.js +176 -0
  59. package/dist/src/{version.js → bootstrap/version.js} +35 -51
  60. package/dist/src/control-plane/application/advance-once.js +184 -0
  61. package/dist/src/control-plane/application/control-plane-projection.js +42 -0
  62. package/dist/src/control-plane/application/control-plane-service.js +42 -0
  63. package/dist/src/control-plane/application/intake-pipeline.js +29 -0
  64. package/dist/src/control-plane/application/runner-control-service.js +66 -0
  65. package/dist/src/control-plane/application/runner-pipeline.js +57 -0
  66. package/dist/src/control-plane/application/schedule-service.js +47 -0
  67. package/dist/src/control-plane/application/work-cancellation-policy.js +31 -0
  68. package/dist/src/control-plane/contracts/commands.js +10 -0
  69. package/dist/src/control-plane/contracts/config.js +22 -0
  70. package/dist/src/control-plane/contracts/events.js +81 -0
  71. package/dist/src/control-plane/contracts/streams.js +5 -0
  72. package/dist/src/control-plane/domain/dispatch-policy.js +15 -0
  73. package/dist/src/control-plane/domain/quota-policy.js +26 -0
  74. package/dist/src/control-plane/domain/runner-quota.js +35 -0
  75. package/dist/src/control-plane/domain/schedule-policy.js +46 -0
  76. package/dist/src/control-plane/index.js +20 -0
  77. package/dist/src/control-plane/infrastructure/intake-host.js +24 -0
  78. package/dist/src/control-plane/infrastructure/resident-host.js +45 -0
  79. package/dist/src/control-plane/infrastructure/tick-host.js +35 -0
  80. package/dist/src/execution/application/activation-claim.js +50 -0
  81. package/dist/src/execution/application/active-run-cancellation.js +14 -0
  82. package/dist/src/execution/application/execution-activity.js +97 -0
  83. package/dist/src/execution/application/execution-projection.js +17 -0
  84. package/dist/src/execution/application/execution-service.js +372 -0
  85. package/dist/src/execution/application/execution-validation.js +26 -0
  86. package/dist/src/execution/application/recovery-service.js +212 -0
  87. package/dist/src/execution/application/run-lifecycle.js +115 -0
  88. package/dist/src/execution/application/run-liveness-service.js +80 -0
  89. package/dist/src/execution/application/run-repository.js +27 -0
  90. package/dist/src/execution/contracts/commands.js +1 -0
  91. package/dist/src/execution/contracts/config.js +35 -0
  92. package/dist/src/execution/contracts/control-plane.js +1 -0
  93. package/dist/src/execution/contracts/event-factory.js +53 -0
  94. package/dist/src/execution/contracts/events.js +274 -0
  95. package/dist/src/execution/contracts/identifiers.js +5 -0
  96. package/dist/src/execution/contracts/liveness.js +1 -0
  97. package/dist/src/execution/contracts/results.js +1 -0
  98. package/dist/src/execution/contracts/runner.js +15 -0
  99. package/dist/src/execution/contracts/streams.js +10 -0
  100. package/dist/src/execution/contracts/views.js +1 -0
  101. package/dist/src/execution/contracts/vocabulary.js +43 -0
  102. package/dist/src/execution/contracts/workspace.js +1 -0
  103. package/dist/src/execution/domain/run-result.js +34 -0
  104. package/dist/src/execution/domain/run.js +142 -0
  105. package/dist/src/execution/index.js +31 -0
  106. package/dist/src/execution/infrastructure/agent-runner-adapter.js +114 -0
  107. package/dist/src/execution/infrastructure/process-execution.js +29 -0
  108. package/dist/src/execution/infrastructure/prompt-templates.js +42 -0
  109. package/dist/src/execution/infrastructure/runners/claude.js +120 -0
  110. package/dist/src/execution/infrastructure/runners/codex.js +143 -0
  111. package/dist/src/execution/infrastructure/runners/command.js +4 -0
  112. package/dist/src/execution/infrastructure/runners/cursor.js +69 -0
  113. package/dist/src/execution/infrastructure/runners/fake-scenarios.js +99 -0
  114. package/dist/src/execution/infrastructure/runners/fake.js +65 -0
  115. package/dist/src/execution/infrastructure/runners/registry.js +26 -0
  116. package/dist/src/execution/infrastructure/transcript-store.js +223 -0
  117. package/dist/src/execution/infrastructure/transcripts.js +11 -0
  118. package/dist/src/execution/infrastructure/workspace/fake-workspace.js +19 -0
  119. package/dist/src/execution/infrastructure/workspace/git-workspace.js +241 -0
  120. package/dist/src/integrations/application/agent-run-publication-reactor.js +111 -0
  121. package/dist/src/integrations/application/artifact-registration-reactor.js +159 -0
  122. package/dist/src/integrations/application/poll-service.js +25 -0
  123. package/dist/src/integrations/application/terminal-agent-run-report.js +40 -0
  124. package/dist/src/integrations/application/work-admission.js +31 -0
  125. package/dist/src/integrations/application/work-conclusion.js +19 -0
  126. package/dist/src/integrations/contracts/artifact-events.js +60 -0
  127. package/dist/src/integrations/contracts/artifact-vocabulary.js +9 -0
  128. package/dist/src/integrations/contracts/config.js +14 -0
  129. package/dist/src/integrations/contracts/identifiers.js +5 -0
  130. package/dist/src/integrations/contracts/intake-rules.js +14 -0
  131. package/dist/src/integrations/contracts/intake.js +1 -0
  132. package/dist/src/integrations/contracts/outcome-vocabulary.js +7 -0
  133. package/dist/src/integrations/contracts/provider.js +37 -0
  134. package/dist/src/integrations/contracts/streams.js +10 -0
  135. package/dist/src/integrations/delivery/application/delivery-outcome-reactor.js +75 -0
  136. package/dist/src/integrations/delivery/application/delivery-projector.js +165 -0
  137. package/dist/src/integrations/delivery/application/delivery-service.js +173 -0
  138. package/dist/src/integrations/delivery/contracts/commands.js +1 -0
  139. package/dist/src/integrations/delivery/contracts/config.js +1 -0
  140. package/dist/src/integrations/delivery/contracts/event-factory.js +18 -0
  141. package/dist/src/integrations/delivery/contracts/events.js +86 -0
  142. package/dist/src/integrations/delivery/contracts/intents.js +89 -0
  143. package/dist/src/integrations/delivery/contracts/views.js +1 -0
  144. package/dist/src/integrations/delivery/contracts/vocabulary.js +22 -0
  145. package/dist/src/integrations/fake/durable-delivery-provider.js +48 -0
  146. package/dist/src/integrations/fake/external-sink.js +9 -0
  147. package/dist/src/integrations/fake/external-source.js +44 -0
  148. package/dist/src/integrations/fake/inbound-translator.js +130 -0
  149. package/dist/src/integrations/fake/provider.js +95 -0
  150. package/dist/src/integrations/github/application/agent-context-reader.js +54 -0
  151. package/dist/src/integrations/github/application/agent-run-comment.js +94 -0
  152. package/dist/src/integrations/github/application/comment-history-reader.js +43 -0
  153. package/dist/src/integrations/github/application/inbound-context.js +10 -0
  154. package/dist/src/integrations/github/application/inbound-review-signals.js +159 -0
  155. package/dist/src/integrations/github/application/inbound-translator.js +231 -0
  156. package/dist/src/integrations/github/application/inbound-watch-gate-signals.js +84 -0
  157. package/dist/src/integrations/github/application/intake-policy.js +23 -0
  158. package/dist/src/integrations/github/application/outbound-translator.js +47 -0
  159. package/dist/src/integrations/github/application/poll-service.js +29 -0
  160. package/dist/src/integrations/github/application/pull-request-translation.js +10 -0
  161. package/dist/src/integrations/github/application/resource-links.js +8 -0
  162. package/dist/src/integrations/github/application/review-command-translator.js +16 -0
  163. package/dist/src/integrations/github/application/wake-labels.js +95 -0
  164. package/dist/src/integrations/github/contracts/config.js +47 -0
  165. package/dist/src/integrations/github/contracts/events.js +127 -0
  166. package/dist/src/integrations/github/contracts/external-key.js +19 -0
  167. package/dist/src/integrations/github/contracts/payloads.js +9 -0
  168. package/dist/src/integrations/github/contracts/vocabulary.js +55 -0
  169. package/dist/src/integrations/github/index.js +31 -0
  170. package/dist/src/integrations/github/infrastructure/client-reads.js +214 -0
  171. package/dist/src/integrations/github/infrastructure/client.js +132 -0
  172. package/dist/src/integrations/github/infrastructure/content-fingerprint.js +10 -0
  173. package/dist/src/integrations/github/infrastructure/delivery.js +31 -0
  174. package/dist/src/integrations/github/infrastructure/etag-cache.js +56 -0
  175. package/dist/src/integrations/github/infrastructure/gh-auth.js +19 -0
  176. package/dist/src/integrations/github/infrastructure/issue-source.js +109 -0
  177. package/dist/src/integrations/github/infrastructure/pr-source.js +186 -0
  178. package/dist/src/integrations/github/infrastructure/review-source.js +70 -0
  179. package/dist/src/integrations/github/infrastructure/source.js +108 -0
  180. package/dist/src/integrations/github/provider.js +96 -0
  181. package/dist/src/integrations/index.js +30 -0
  182. package/dist/src/kernel/contracts/checkpoint-store.js +1 -0
  183. package/dist/src/kernel/contracts/clock.js +1 -0
  184. package/dist/src/kernel/contracts/commands.js +1 -0
  185. package/dist/src/kernel/contracts/event-journal.js +2 -0
  186. package/dist/src/kernel/contracts/event-schema.js +54 -0
  187. package/dist/src/kernel/contracts/events.js +11 -0
  188. package/dist/src/kernel/contracts/id-generator.js +1 -0
  189. package/dist/src/kernel/contracts/identifiers.js +8 -0
  190. package/dist/src/kernel/contracts/projection-store.js +1 -0
  191. package/dist/src/kernel/contracts/relations.js +1 -0
  192. package/dist/src/kernel/contracts/schema.js +17 -0
  193. package/dist/src/kernel/contracts/vocabulary.js +6 -0
  194. package/dist/src/kernel/domain/event-envelope.js +29 -0
  195. package/dist/src/kernel/domain/match-mode.js +11 -0
  196. package/dist/src/kernel/domain/relation.js +10 -0
  197. package/dist/src/kernel/index.js +17 -0
  198. package/dist/src/{lib/clock.js → kernel/infrastructure/system-clock.js} +3 -3
  199. package/dist/src/kernel/infrastructure/ulid-id-generator.js +9 -0
  200. package/dist/src/main.js +137 -1030
  201. package/dist/src/orchestration/application/accept-activity-outcome.js +25 -0
  202. package/dist/src/orchestration/application/accept-signal.js +35 -0
  203. package/dist/src/orchestration/application/advance-workflow.js +160 -0
  204. package/dist/src/orchestration/application/coordination-claims.js +107 -0
  205. package/dist/src/orchestration/application/group-budget-recorder.js +43 -0
  206. package/dist/src/orchestration/application/orchestration-projection.js +20 -0
  207. package/dist/src/orchestration/application/orchestration-repository.js +36 -0
  208. package/dist/src/orchestration/application/orchestration-service.js +82 -0
  209. package/dist/src/orchestration/application/request-child.js +118 -0
  210. package/dist/src/orchestration/application/signal-reactor.js +17 -0
  211. package/dist/src/orchestration/application/start-workflow.js +51 -0
  212. package/dist/src/orchestration/application/watch-reactor.js +82 -0
  213. package/dist/src/orchestration/contracts/activity-outcome.js +18 -0
  214. package/dist/src/orchestration/contracts/commands.js +1 -0
  215. package/dist/src/orchestration/contracts/config.js +128 -0
  216. package/dist/src/orchestration/contracts/event-decoder.js +263 -0
  217. package/dist/src/orchestration/contracts/events.js +41 -0
  218. package/dist/src/orchestration/contracts/identifiers.js +35 -0
  219. package/dist/src/orchestration/contracts/streams.js +38 -0
  220. package/dist/src/orchestration/contracts/views.js +1 -0
  221. package/dist/src/orchestration/contracts/vocabulary.js +30 -0
  222. package/dist/src/orchestration/domain/activation-policy.js +55 -0
  223. package/dist/src/orchestration/domain/approval-defaults.js +17 -0
  224. package/dist/src/orchestration/domain/child-policy.js +63 -0
  225. package/dist/src/orchestration/domain/compiler.js +162 -0
  226. package/dist/src/orchestration/domain/coordination-events.js +25 -0
  227. package/dist/src/orchestration/domain/decision-events.js +47 -0
  228. package/dist/src/orchestration/domain/interpreter.js +49 -0
  229. package/dist/src/orchestration/domain/operator-retry-policy.js +34 -0
  230. package/dist/src/orchestration/domain/retry-policy.js +26 -0
  231. package/dist/src/orchestration/domain/signal-policy.js +120 -0
  232. package/dist/src/orchestration/domain/supplemental-policy.js +61 -0
  233. package/dist/src/orchestration/domain/transition.js +55 -0
  234. package/dist/src/orchestration/domain/workflow-graph.js +41 -0
  235. package/dist/src/orchestration/domain/workflow-instance-events.js +236 -0
  236. package/dist/src/orchestration/domain/workflow-instance.js +41 -0
  237. package/dist/src/orchestration/domain/workflow-selector.js +21 -0
  238. package/dist/src/orchestration/index.js +19 -0
  239. package/dist/src/persistence/application/projection-runner.js +79 -0
  240. package/dist/src/persistence/filesystem/file-checkpoint-store.js +35 -0
  241. package/dist/src/persistence/filesystem/file-event-journal.js +143 -0
  242. package/dist/src/persistence/filesystem/file-lock.js +91 -0
  243. package/dist/src/persistence/filesystem/file-projection-store.js +64 -0
  244. package/dist/src/persistence/index.js +8 -0
  245. package/dist/src/persistence/memory/in-memory-checkpoint-store.js +14 -0
  246. package/dist/src/persistence/memory/in-memory-event-journal.js +88 -0
  247. package/dist/src/persistence/memory/in-memory-projection-store.js +17 -0
  248. package/dist/src/resources/application/lookup-projections.js +48 -0
  249. package/dist/src/resources/application/resource-lookup.js +16 -0
  250. package/dist/src/resources/application/resource-projections.js +81 -0
  251. package/dist/src/resources/application/resource-repository.js +18 -0
  252. package/dist/src/resources/application/resource-service.js +83 -0
  253. package/dist/src/resources/contracts/commands.js +1 -0
  254. package/dist/src/resources/contracts/config.js +2 -0
  255. package/dist/src/resources/contracts/events.js +83 -0
  256. package/dist/src/resources/contracts/identifiers.js +15 -0
  257. package/dist/src/resources/contracts/streams.js +6 -0
  258. package/dist/src/resources/contracts/views.js +1 -0
  259. package/dist/src/resources/contracts/vocabulary.js +67 -0
  260. package/dist/src/resources/domain/correlation.js +8 -0
  261. package/dist/src/resources/domain/resource.js +58 -0
  262. package/dist/src/resources/index.js +14 -0
  263. package/dist/src/surfaces/api/contracts/activities.js +1 -0
  264. package/dist/src/surfaces/api/contracts/board.js +2 -0
  265. package/dist/src/surfaces/api/contracts/common.js +46 -0
  266. package/dist/src/surfaces/api/contracts/control-plane.js +5 -0
  267. package/dist/src/surfaces/api/contracts/events.js +1 -0
  268. package/dist/src/surfaces/api/contracts/execution.js +1 -0
  269. package/dist/src/surfaces/api/contracts/index.js +13 -0
  270. package/dist/src/surfaces/api/contracts/observability.js +1 -0
  271. package/dist/src/surfaces/api/contracts/orchestration.js +1 -0
  272. package/dist/src/surfaces/api/contracts/resources.js +1 -0
  273. package/dist/src/surfaces/api/contracts/status.js +1 -0
  274. package/dist/src/surfaces/api/contracts/system.js +1 -0
  275. package/dist/src/surfaces/api/contracts/transport-values.js +39 -0
  276. package/dist/src/surfaces/api/contracts/work.js +17 -0
  277. package/dist/src/surfaces/api/http-server.js +121 -0
  278. package/dist/src/surfaces/api/presenters/activities.js +1 -0
  279. package/dist/src/surfaces/api/presenters/board.js +3 -0
  280. package/dist/src/surfaces/api/presenters/control-plane.js +1 -0
  281. package/dist/src/surfaces/api/presenters/events.js +1 -0
  282. package/dist/src/surfaces/api/presenters/execution.js +59 -0
  283. package/dist/src/surfaces/api/presenters/observability.js +1 -0
  284. package/dist/src/surfaces/api/presenters/orchestration.js +19 -0
  285. package/dist/src/surfaces/api/presenters/resources.js +15 -0
  286. package/dist/src/surfaces/api/presenters/status.js +3 -0
  287. package/dist/src/surfaces/api/presenters/system.js +14 -0
  288. package/dist/src/surfaces/api/presenters/work.js +16 -0
  289. package/dist/src/surfaces/api/problem-details.js +9 -0
  290. package/dist/src/surfaces/api/router.js +13 -0
  291. package/dist/src/surfaces/api/routes/activities.js +1 -0
  292. package/dist/src/surfaces/api/routes/applications.js +1 -0
  293. package/dist/src/surfaces/api/routes/commands.js +82 -0
  294. package/dist/src/surfaces/api/routes/control-plane.js +6 -0
  295. package/dist/src/surfaces/api/routes/events.js +1 -0
  296. package/dist/src/surfaces/api/routes/execution.js +8 -0
  297. package/dist/src/surfaces/api/routes/index.js +18 -0
  298. package/dist/src/surfaces/api/routes/observability.js +1 -0
  299. package/dist/src/surfaces/api/routes/orchestration.js +1 -0
  300. package/dist/src/surfaces/api/routes/pagination.js +78 -0
  301. package/dist/src/surfaces/api/routes/read.js +135 -0
  302. package/dist/src/surfaces/api/routes/resources.js +1 -0
  303. package/dist/src/surfaces/api/routes/responses.js +67 -0
  304. package/dist/src/surfaces/api/routes/system.js +1 -0
  305. package/dist/src/surfaces/api/routes/work.js +9 -0
  306. package/dist/src/surfaces/cli/commands/api.js +1 -0
  307. package/dist/src/surfaces/cli/commands/audit.js +10 -0
  308. package/dist/src/surfaces/cli/commands/correlate.js +2 -0
  309. package/dist/src/surfaces/cli/commands/doctor.js +6 -0
  310. package/dist/src/surfaces/cli/commands/init.js +39 -0
  311. package/dist/src/surfaces/cli/commands/sandbox-entrypoint.js +33 -0
  312. package/dist/src/surfaces/cli/commands/sandbox-setup.js +29 -0
  313. package/dist/src/surfaces/cli/commands/sandbox.js +105 -0
  314. package/dist/src/surfaces/cli/commands/self-update.js +38 -0
  315. package/dist/src/surfaces/cli/commands/smoke.js +16 -0
  316. package/dist/src/surfaces/cli/commands/start.js +1 -0
  317. package/dist/src/surfaces/cli/commands/stop.js +13 -0
  318. package/dist/src/surfaces/cli/commands/tick.js +1 -0
  319. package/dist/src/surfaces/cli/commands/ui.js +2 -0
  320. package/dist/src/surfaces/cli/commands/validate-state.js +5 -0
  321. package/dist/src/surfaces/cli/infrastructure/docker-cli.js +221 -0
  322. package/dist/src/surfaces/cli/infrastructure/process-log.js +86 -0
  323. package/dist/src/surfaces/cli/main.js +140 -0
  324. package/dist/src/surfaces/cli/usage.js +33 -0
  325. package/dist/src/surfaces/contracts/config.js +34 -0
  326. package/dist/src/surfaces/index.js +47 -0
  327. package/dist/src/surfaces/web-assets/assets/index-Bfn4FwZG.css +1 -0
  328. package/dist/src/surfaces/web-assets/assets/index-ipB6JPGU.js +1090 -0
  329. package/dist/src/surfaces/web-assets/index.html +13 -0
  330. package/dist/src/surfaces/web-host/asset-source.js +1 -0
  331. package/dist/src/surfaces/web-host/packaged-assets.js +44 -0
  332. package/dist/src/work/application/work-projection.js +59 -0
  333. package/dist/src/work/application/work-repository.js +18 -0
  334. package/dist/src/work/application/work-service.js +117 -0
  335. package/dist/src/work/contracts/commands.js +1 -0
  336. package/dist/src/work/contracts/config.js +2 -0
  337. package/dist/src/work/contracts/events.js +103 -0
  338. package/dist/src/work/contracts/identifiers.js +5 -0
  339. package/dist/src/work/contracts/streams.js +6 -0
  340. package/dist/src/work/contracts/views.js +1 -0
  341. package/dist/src/work/contracts/vocabulary.js +6 -0
  342. package/dist/src/work/domain/work-item.js +89 -0
  343. package/dist/src/work/index.js +11 -0
  344. package/docker/Dockerfile +10 -6
  345. package/docker/Dockerfile.packaged +3 -3
  346. package/package.json +29 -4
  347. package/prompts/implement.md +2 -2
  348. package/dist/src/adapters/claude/claude-runner.js +0 -529
  349. package/dist/src/adapters/codex/codex-runner.js +0 -503
  350. package/dist/src/adapters/cursor/cursor-runner.js +0 -418
  351. package/dist/src/adapters/docker/docker-cli.js +0 -150
  352. package/dist/src/adapters/fake/fake-artifact-verifier.js +0 -14
  353. package/dist/src/adapters/fake/fake-github-pull-request-activity-source.js +0 -77
  354. package/dist/src/adapters/fake/fake-resource-index.js +0 -21
  355. package/dist/src/adapters/fake/fake-runner.js +0 -77
  356. package/dist/src/adapters/fake/fake-ticketing-system.js +0 -185
  357. package/dist/src/adapters/fake/fake-workspace-manager.js +0 -81
  358. package/dist/src/adapters/fs/resource-index.js +0 -126
  359. package/dist/src/adapters/fs/self-update-ledger.js +0 -17
  360. package/dist/src/adapters/fs/state-store.js +0 -775
  361. package/dist/src/adapters/git/git-workspace-manager.js +0 -289
  362. package/dist/src/adapters/github/github-artifact-verifier.js +0 -38
  363. package/dist/src/adapters/github/github-auth.js +0 -16
  364. package/dist/src/adapters/github/github-client.js +0 -249
  365. package/dist/src/adapters/github/github-etag-cache.js +0 -57
  366. package/dist/src/adapters/github/github-issues-work-source.js +0 -692
  367. package/dist/src/adapters/github/github-pull-request-activity-source.js +0 -584
  368. package/dist/src/adapters/github/github-pull-request-merge-actor.js +0 -45
  369. package/dist/src/adapters/http/ui-assets.js +0 -1188
  370. package/dist/src/adapters/http/ui-data.js +0 -1107
  371. package/dist/src/adapters/http/ui-server.js +0 -458
  372. package/dist/src/adapters/runner/cli-command.js +0 -73
  373. package/dist/src/adapters/runner/prompt-templates.js +0 -76
  374. package/dist/src/adapters/runner/runner-cli-adapter.js +0 -112
  375. package/dist/src/adapters/runner/runner-registry.js +0 -139
  376. package/dist/src/adapters/runner/runtime-events.js +0 -32
  377. package/dist/src/adapters/runner/stage-prompt.js +0 -268
  378. package/dist/src/adapters/runner/transcripts.js +0 -25
  379. package/dist/src/cli/audit-command.js +0 -36
  380. package/dist/src/cli/correlate-command.js +0 -62
  381. package/dist/src/cli/doctor-command.js +0 -48
  382. package/dist/src/cli/init-command.js +0 -18
  383. package/dist/src/cli/sandbox-command.js +0 -258
  384. package/dist/src/cli/sandbox-entrypoint-command.js +0 -88
  385. package/dist/src/cli/sandbox-exec-logging.js +0 -7
  386. package/dist/src/cli/sandbox-resume.js +0 -79
  387. package/dist/src/cli/sandbox-setup-command.js +0 -31
  388. package/dist/src/cli/scaffold-assets.js +0 -100
  389. package/dist/src/cli/self-update-command.js +0 -198
  390. package/dist/src/cli/startup-preflight.js +0 -134
  391. package/dist/src/cli/stop-command.js +0 -46
  392. package/dist/src/cli/ui-command.js +0 -27
  393. package/dist/src/config/defaults.js +0 -10
  394. package/dist/src/config/discover-config-files.js +0 -11
  395. package/dist/src/config/load-config.js +0 -69
  396. package/dist/src/config/split-config.js +0 -14
  397. package/dist/src/core/active-run-recovery.js +0 -44
  398. package/dist/src/core/approval-intents.js +0 -16
  399. package/dist/src/core/audit-events.js +0 -82
  400. package/dist/src/core/control-plane.js +0 -192
  401. package/dist/src/core/event-builders.js +0 -153
  402. package/dist/src/core/event-resolver.js +0 -249
  403. package/dist/src/core/lifecycle-service.js +0 -8
  404. package/dist/src/core/live-execution.js +0 -81
  405. package/dist/src/core/outbox.js +0 -198
  406. package/dist/src/core/policy-engine.js +0 -389
  407. package/dist/src/core/projection-updater.js +0 -693
  408. package/dist/src/core/quota-backoff.js +0 -69
  409. package/dist/src/core/run-lease.js +0 -38
  410. package/dist/src/core/scheduled-workflow-source.js +0 -199
  411. package/dist/src/core/sink-router.js +0 -107
  412. package/dist/src/core/stale-run-reconciler.js +0 -352
  413. package/dist/src/core/tick-runner.js +0 -2442
  414. package/dist/src/core/workspace-cleanup.js +0 -117
  415. package/dist/src/domain/branch-naming.js +0 -14
  416. package/dist/src/domain/custom-commands.js +0 -58
  417. package/dist/src/domain/event-types.js +0 -158
  418. package/dist/src/domain/manual-labels.js +0 -1
  419. package/dist/src/domain/resource-uri.js +0 -38
  420. package/dist/src/domain/runner-routing.js +0 -127
  421. package/dist/src/domain/runtime-events.js +0 -36
  422. package/dist/src/domain/schema.js +0 -1233
  423. package/dist/src/domain/sources.js +0 -16
  424. package/dist/src/domain/stages.js +0 -36
  425. package/dist/src/domain/work-item-labels.js +0 -48
  426. package/dist/src/domain/work-item-lifecycle.js +0 -10
  427. package/dist/src/domain/work-item-status.js +0 -33
  428. package/dist/src/domain/workflows.js +0 -159
  429. package/dist/src/lib/deep-merge.js +0 -14
  430. package/dist/src/lib/detached-process-logging.js +0 -46
  431. package/dist/src/lib/event-log.js +0 -21
  432. package/dist/src/lib/format.js +0 -41
  433. package/dist/src/lib/json-file.js +0 -23
  434. package/dist/src/lib/lock.js +0 -181
  435. package/dist/src/lib/log-rotation.js +0 -42
  436. package/dist/src/lib/paths.js +0 -52
  437. package/dist/src/lib/process-identity.js +0 -54
  438. package/dist/src/lib/state-health.js +0 -30
  439. package/dist/src/lib/work-id.js +0 -19
  440. package/dist/src/lib/yaml-file.js +0 -20
  441. package/templates/SETUP.md +0 -115
  442. /package/dist/src/{core → activities/pr}/contracts.js +0 -0
  443. /package/dist/src/{domain/types.js → control-plane/contracts/views.js} +0 -0
@@ -1,693 +0,0 @@
1
- import { CORRELATION_PRIMARY_CONFLICT_EVENT, CORRELATION_REGISTERED_EVENT, CORRELATION_RETRACTED_EVENT, RETRY_REQUESTED_EVENT, RUN_REQUESTED_EVENT, RUN_CLAIMED_EVENT, RUN_COMPLETED_EVENT, WORKFLOW_SELECTED_EVENT, WORK_ITEM_DELETED_EVENT, WORK_ITEM_FROZEN_EVENT, WORK_ITEM_UNFROZEN_EVENT, WORKSPACE_CLEANED_EVENT, } from '../domain/event-types.js';
2
- import { UNRESOLVED_WORK_ITEM_KEY, parseIssueStateRecord } from '../domain/schema.js';
3
- import { doneRunnerSentinel, rejectedRunnerSentinel, stageFromLabels } from '../domain/stages.js';
4
- import { FROZEN_WORK_ITEM_LABEL } from '../domain/work-item-lifecycle.js';
5
- import { workItemStatusForRunOutcome } from '../domain/work-item-status.js';
6
- import { builtInDefaultWorkflowDefinition, defaultWorkflowName, selectWorkflowForEvent, workflowStageVocabulary, } from '../domain/workflows.js';
7
- import { isCustomCommandAction } from '../domain/custom-commands.js';
8
- import { createEventEnvelope } from '../lib/event-log.js';
9
- function stringArrayFromPayload(value) {
10
- return Array.isArray(value)
11
- ? value.filter((entry) => typeof entry === 'string')
12
- : [];
13
- }
14
- function configuredStagesForLabels(config) {
15
- if (config === undefined) {
16
- return workflowStageVocabulary(builtInDefaultWorkflowDefinition);
17
- }
18
- const workflow = config.workflows[defaultWorkflowName(config)];
19
- return workflow === undefined ? undefined : workflowStageVocabulary(workflow);
20
- }
21
- function createProjectionFromIssueEvent(event, config) {
22
- const issue = event.sourceEventType === 'ticket.upsert' ? event.payload.ticket : event.payload.issue;
23
- if (issue === undefined || typeof issue !== 'object' || issue === null) {
24
- return null;
25
- }
26
- const labels = Array.isArray(issue.labels)
27
- ? issue.labels
28
- : [];
29
- // Only the scheduled-workflow-source's synthetic ticket carries a
30
- // wake:schedule resourceUri; no other mint path sets this, so `scheduled`
31
- // has a real backing context field instead of only ever living in a label.
32
- const scheduled = event.sourceRefs.resourceUri?.split(':')[1] === 'schedule';
33
- return parseIssueStateRecord({
34
- schemaVersion: 1,
35
- workItemKey: event.workItemKey,
36
- origin: event.sourceSystem,
37
- issue,
38
- wake: {
39
- stage: stageFromLabels(labels, configuredStagesForLabels(config)) ?? 'queue',
40
- stageHistory: [],
41
- recentEventIds: [event.eventId],
42
- syncedAt: event.ingestedAt,
43
- },
44
- context: scheduled ? { scheduled: true } : {},
45
- });
46
- }
47
- async function pinSelectedWorkflow(projection, event, ctx, config) {
48
- const context = projection.context;
49
- if (config === undefined || typeof context.workflow === 'string') {
50
- return projection;
51
- }
52
- const workflow = selectWorkflowForEvent(event, config);
53
- if (workflow === null) {
54
- return projection;
55
- }
56
- const selectedEvent = await ctx.appendEvent(createEventEnvelope({
57
- eventId: `workflow-selected-${projection.workItemKey}`,
58
- workItemKey: projection.workItemKey,
59
- streamScope: 'work-item',
60
- direction: 'internal',
61
- sourceSystem: 'wake',
62
- sourceEventType: WORKFLOW_SELECTED_EVENT,
63
- sourceRefs: event.sourceRefs,
64
- occurredAt: event.occurredAt,
65
- ingestedAt: event.ingestedAt,
66
- trigger: 'context-only',
67
- payload: {
68
- workflow,
69
- selectedFromEventId: event.eventId,
70
- },
71
- }));
72
- return applyEvent(projection, selectedEvent, ctx, config);
73
- }
74
- async function applyEvent(current, event, ctx, config) {
75
- // The shared sentinel for events whose resource failed mint qualification
76
- // (tick-runner.ts's resolveInboundEvent, spec D1'). These are durable and
77
- // inspectable via the event log but must never materialize a projection —
78
- // otherwise every unqualified ticket.upsert/fake.issue.upsert would create
79
- // one here on its first sighting (current === null), the same as a real
80
- // mint, defeating the entire point of the gate. `current` is always null
81
- // for this key (readIssueState(UNRESOLVED_WORK_ITEM_KEY) never has a file
82
- // to read), so returning it unchanged is equivalent to "no projection".
83
- if (event.workItemKey === UNRESOLVED_WORK_ITEM_KEY) {
84
- return current;
85
- }
86
- if (event.sourceEventType === 'fake.issue.upsert' || event.sourceEventType === 'ticket.upsert') {
87
- const next = createProjectionFromIssueEvent(event, config);
88
- if (next === null) {
89
- return current;
90
- }
91
- if (current === null) {
92
- return pinSelectedWorkflow(next, event, ctx, config);
93
- }
94
- const nextStageFromLabels = stageFromLabels(next.issue.labels, configuredStagesForLabels(config));
95
- const shouldReconcileStage = nextStageFromLabels !== undefined && nextStageFromLabels !== current.wake.stage;
96
- const updated = parseIssueStateRecord({
97
- ...current,
98
- origin: current.origin,
99
- issue: next.issue,
100
- wake: {
101
- ...current.wake,
102
- ...(shouldReconcileStage ? { stage: nextStageFromLabels } : {}),
103
- syncedAt: event.ingestedAt,
104
- stageHistory: shouldReconcileStage
105
- ? [
106
- ...current.wake.stageHistory,
107
- {
108
- stage: nextStageFromLabels,
109
- changedAt: event.occurredAt,
110
- reason: 'github-label-sync',
111
- },
112
- ]
113
- : current.wake.stageHistory,
114
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
115
- },
116
- });
117
- return pinSelectedWorkflow(updated, event, ctx, config);
118
- }
119
- if (current === null) {
120
- return null;
121
- }
122
- if (event.sourceEventType === WORKFLOW_SELECTED_EVENT) {
123
- const workflow = event.payload.workflow;
124
- if (typeof workflow !== 'string') {
125
- return current;
126
- }
127
- return parseIssueStateRecord({
128
- ...current,
129
- context: {
130
- ...current.context,
131
- workflow,
132
- },
133
- wake: {
134
- ...current.wake,
135
- syncedAt: event.ingestedAt,
136
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
137
- },
138
- });
139
- }
140
- if (event.sourceEventType === 'fake.issue.comment.created' ||
141
- event.sourceEventType === 'ticket.comment.created' ||
142
- event.sourceEventType === 'ticket.comment.updated' ||
143
- event.sourceEventType === 'pr.comment.created' ||
144
- event.sourceEventType === 'pr.review.created' ||
145
- event.sourceEventType === 'pr.review-comment.created' ||
146
- event.sourceEventType === 'pr.checks.failed') {
147
- const comment = event.payload.comment;
148
- if (comment === undefined || typeof comment !== 'object' || comment === null) {
149
- return current;
150
- }
151
- const isBotAuthored = Boolean(event.derivedHints?.botAuthoredComment);
152
- const nextComment = {
153
- ...comment,
154
- isBotAuthored,
155
- };
156
- const existingComments = current.comments.filter((entry) => entry.id !== String(comment.id));
157
- return parseIssueStateRecord({
158
- ...current,
159
- comments: [...existingComments, nextComment],
160
- latestComment: nextComment,
161
- wake: {
162
- ...current.wake,
163
- syncedAt: event.ingestedAt,
164
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
165
- },
166
- });
167
- }
168
- if (event.sourceEventType === RUN_CLAIMED_EVENT) {
169
- const payload = event.payload;
170
- if (payload.claimedStage === undefined) {
171
- return parseIssueStateRecord({
172
- ...current,
173
- wake: {
174
- ...current.wake,
175
- syncedAt: event.ingestedAt,
176
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
177
- },
178
- });
179
- }
180
- return parseIssueStateRecord({
181
- ...current,
182
- context: {
183
- ...current.context,
184
- status: 'working',
185
- },
186
- wake: {
187
- ...current.wake,
188
- stage: payload.claimedStage,
189
- lastRunId: event.sourceRefs.runId ?? current.wake.lastRunId,
190
- syncedAt: event.ingestedAt,
191
- stageHistory: [
192
- ...current.wake.stageHistory,
193
- {
194
- stage: payload.claimedStage,
195
- changedAt: event.occurredAt,
196
- reason: `run:${payload.action ?? 'unknown'}:claimed`,
197
- },
198
- ],
199
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
200
- },
201
- });
202
- }
203
- if (event.sourceEventType === RUN_COMPLETED_EVENT) {
204
- const payload = event.payload;
205
- // A historical event stream may still carry the retired AWAITING_APPROVAL
206
- // sentinel (pre-ADR-0002). Normalize it here, once, so every branch below
207
- // and the strict runnerSentinelSchema validation on new writes to
208
- // context.lastRunSentinel only ever see the current four-value
209
- // vocabulary — replaying an old event stream must still land on the same
210
- // practical state (approval-gated) as it did before this change.
211
- const isLegacyAwaitingApproval = payload.sentinel === 'AWAITING_APPROVAL';
212
- const sentinel = isLegacyAwaitingApproval ? doneRunnerSentinel : payload.sentinel;
213
- const approvalGated = payload.approvalGated === true || isLegacyAwaitingApproval;
214
- // A rejecting plan-review/pr-review watcher sub-run (§5, trigger 1) or a
215
- // human /changes reply (§5, trigger 2) folds onto the parent's context
216
- // without touching wake.stage/lastRunId/session — same isolation
217
- // guarantee PR #479 established for watcher runs generally. Checked
218
- // before the watcherRun early-return below so a rejecting watcher run
219
- // (which also carries watcherRun: true) still updates status/feedback,
220
- // while every other watcher-run completion (e.g. an approving DONE
221
- // verdict) still hits that early-return untouched.
222
- if (payload.changesRequested === true) {
223
- const currentChangesRequestedCount = typeof current.context.changesRequestedCount === 'number' &&
224
- Number.isInteger(current.context.changesRequestedCount)
225
- ? current.context.changesRequestedCount
226
- : 0;
227
- const nextChangesRequestedCount = currentChangesRequestedCount + 1;
228
- const maxRetries = config?.retry.maxChangesRequestedRetries ?? Infinity;
229
- const escalate = nextChangesRequestedCount > maxRetries;
230
- return parseIssueStateRecord({
231
- ...current,
232
- context: {
233
- ...current.context,
234
- status: escalate ? 'blocked' : 'changes-requested',
235
- changesRequestedCount: nextChangesRequestedCount,
236
- ...(payload.reviewFeedbackBody === undefined
237
- ? {}
238
- : { changesRequestedFeedback: payload.reviewFeedbackBody }),
239
- ...(payload.handledCommentId === undefined
240
- ? {}
241
- : { lastHandledCommentId: payload.handledCommentId }),
242
- },
243
- wake: {
244
- ...current.wake,
245
- syncedAt: event.ingestedAt,
246
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
247
- },
248
- });
249
- }
250
- if (payload.watcherRun === true) {
251
- return parseIssueStateRecord({
252
- ...current,
253
- wake: {
254
- ...current.wake,
255
- syncedAt: event.ingestedAt,
256
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
257
- },
258
- });
259
- }
260
- // Clear the session when the stage moves forward (new action needed) or the
261
- // run failed outright. Keep it for BLOCKED so the same action can resume
262
- // the in-progress session after a human replies.
263
- const isForwardProgression = payload.nextStage !== undefined && payload.nextStage !== current.wake.stage;
264
- const stageChanged = payload.nextStage !== undefined && payload.nextStage !== current.wake.stage;
265
- const isFailed = sentinel === 'FAILED';
266
- const isCompletedCustomCommand = payload.action !== undefined &&
267
- sentinel === doneRunnerSentinel &&
268
- config !== undefined &&
269
- isCustomCommandAction(payload.action, config);
270
- const shouldClearSession = isForwardProgression || isFailed;
271
- const hasPendingChangesRequested = (typeof current.context.changesRequestedCount === 'number' &&
272
- current.context.changesRequestedCount > 0) ||
273
- current.context.changesRequestedFeedback !== undefined;
274
- const resolvesChangesRequested = sentinel === doneRunnerSentinel && (!hasPendingChangesRequested || !approvalGated);
275
- const currentFailureCount = typeof current.context.failureCount === 'number' &&
276
- Number.isInteger(current.context.failureCount)
277
- ? current.context.failureCount
278
- : 0;
279
- const nextContext = {
280
- ...current.context,
281
- lastFailureClass: payload.failureClass,
282
- failureCount: payload.failureClass !== undefined
283
- ? currentFailureCount + 1
284
- : sentinel === doneRunnerSentinel || sentinel === rejectedRunnerSentinel
285
- ? 0
286
- : currentFailureCount,
287
- ...(payload.handledCommentId === undefined
288
- ? {}
289
- : { lastHandledCommentId: payload.handledCommentId }),
290
- ...(sentinel === undefined || isCompletedCustomCommand ? {} : { lastRunSentinel: sentinel }),
291
- ...(payload.action === undefined || isCompletedCustomCommand
292
- ? {}
293
- : { lastRunAction: payload.action }),
294
- ...(sentinel === doneRunnerSentinel &&
295
- payload.action !== undefined &&
296
- !isCompletedCustomCommand
297
- ? { lastCompletedAction: payload.action }
298
- : {}),
299
- // Remembered so the approval path knows which action to resume or
300
- // skip when a human posts /approved. Set only for an approval-gated
301
- // DONE — REJECTED/BLOCKED/FAILED never gate on human sign-off this way.
302
- ...(sentinel === doneRunnerSentinel && approvalGated && payload.action !== undefined
303
- ? { pendingApprovalAction: payload.action }
304
- : {}),
305
- ...(sentinel === doneRunnerSentinel && approvalGated
306
- ? { pendingApprovalAllowAutoApproval: payload.allowAutoApproval === true }
307
- : {}),
308
- ...(payload.executionOutcome !== undefined
309
- ? { lastExecutionOutcome: payload.executionOutcome }
310
- : {}),
311
- ...(payload.workflowOutcome !== undefined
312
- ? { lastWorkflowOutcome: payload.workflowOutcome }
313
- : {}),
314
- ...(payload.failurePhase !== undefined ? { lastFailurePhase: payload.failurePhase } : {}),
315
- ...(payload.processStarted !== undefined
316
- ? { lastProcessStarted: payload.processStarted }
317
- : {}),
318
- ...(payload.workspaceChanged !== undefined
319
- ? { lastWorkspaceChanged: payload.workspaceChanged }
320
- : {}),
321
- ...(payload.externalSideEffects !== undefined
322
- ? { lastExternalSideEffects: payload.externalSideEffects }
323
- : {}),
324
- ...(payload.retrySafety !== undefined ? { lastRetrySafety: payload.retrySafety } : {}),
325
- ...(sentinel === undefined || isCompletedCustomCommand
326
- ? {}
327
- : {
328
- status: workItemStatusForRunOutcome({
329
- sentinel: sentinel,
330
- stage: payload.nextStage ?? current.wake.stage,
331
- approvalGated,
332
- }),
333
- }),
334
- // An approval-gated DONE after changes-requested only resubmits work for
335
- // review; the next reviewer verdict decides whether the request was
336
- // actually resolved. Keep the retry counter through that gate so
337
- // repeated rejections can hit the configured escalation cap.
338
- ...(resolvesChangesRequested
339
- ? { changesRequestedCount: 0, changesRequestedFeedback: undefined }
340
- : {}),
341
- };
342
- if (sentinel === 'BLOCKED' || sentinel === 'FAILED') {
343
- nextContext.blockedFromStage = current.wake.stage;
344
- }
345
- else if (sentinel !== undefined) {
346
- delete nextContext.blockedFromStage;
347
- }
348
- if (!(sentinel === doneRunnerSentinel && approvalGated) && !isCompletedCustomCommand) {
349
- delete nextContext.pendingApprovalAction;
350
- delete nextContext.pendingApprovalAllowAutoApproval;
351
- }
352
- return parseIssueStateRecord({
353
- ...current,
354
- context: nextContext,
355
- wake: {
356
- ...current.wake,
357
- stage: payload.nextStage ?? current.wake.stage,
358
- lastRunId: payload.runId ?? current.wake.lastRunId,
359
- sessionId: shouldClearSession ? undefined : (payload.sessionId ?? current.wake.sessionId),
360
- sessionCli: shouldClearSession
361
- ? undefined
362
- : (payload.sessionCli ?? current.wake.sessionCli),
363
- workspacePath: payload.workspacePath ?? current.wake.workspacePath,
364
- blockReason: payload.blockReason ?? current.wake.blockReason,
365
- syncedAt: event.ingestedAt,
366
- stageHistory: stageChanged
367
- ? [
368
- ...current.wake.stageHistory,
369
- {
370
- stage: payload.nextStage,
371
- changedAt: event.occurredAt,
372
- reason: payload.reason ?? event.sourceEventType,
373
- },
374
- ]
375
- : current.wake.stageHistory,
376
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
377
- },
378
- });
379
- }
380
- if (event.sourceEventType === 'ticket.reply.published') {
381
- const commentId = event.sourceRefs.commentId;
382
- if (commentId === undefined) {
383
- return parseIssueStateRecord({
384
- ...current,
385
- wake: {
386
- ...current.wake,
387
- syncedAt: event.ingestedAt,
388
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
389
- },
390
- });
391
- }
392
- return parseIssueStateRecord({
393
- ...current,
394
- wake: {
395
- ...current.wake,
396
- expectedEcho: {
397
- ...current.wake.expectedEcho,
398
- commentIds: Array.from(new Set([...current.wake.expectedEcho.commentIds, commentId])),
399
- },
400
- syncedAt: event.ingestedAt,
401
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
402
- },
403
- });
404
- }
405
- if (event.sourceEventType === RETRY_REQUESTED_EVENT) {
406
- const nextContext = { ...current.context };
407
- delete nextContext.lastRunSentinel;
408
- delete nextContext.lastFailureClass;
409
- delete nextContext.blockedFromStage;
410
- return parseIssueStateRecord({
411
- ...current,
412
- context: nextContext,
413
- wake: {
414
- ...current.wake,
415
- syncedAt: event.ingestedAt,
416
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
417
- },
418
- });
419
- }
420
- if (event.sourceEventType === RUN_REQUESTED_EVENT) {
421
- return parseIssueStateRecord({
422
- ...current,
423
- wake: {
424
- ...current.wake,
425
- syncedAt: event.ingestedAt,
426
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
427
- },
428
- });
429
- }
430
- if (event.sourceEventType === WORK_ITEM_DELETED_EVENT) {
431
- return parseIssueStateRecord({
432
- ...current,
433
- context: {
434
- ...current.context,
435
- deleted: {
436
- at: event.occurredAt,
437
- by: typeof event.payload.requestedBy === 'string' ? event.payload.requestedBy : 'unknown',
438
- },
439
- },
440
- wake: {
441
- ...current.wake,
442
- sessionId: undefined,
443
- sessionCli: undefined,
444
- syncedAt: event.ingestedAt,
445
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
446
- },
447
- });
448
- }
449
- if (event.sourceEventType === WORK_ITEM_FROZEN_EVENT) {
450
- return parseIssueStateRecord({
451
- ...current,
452
- issue: {
453
- ...current.issue,
454
- labels: Array.from(new Set([...current.issue.labels, FROZEN_WORK_ITEM_LABEL])),
455
- },
456
- context: {
457
- ...current.context,
458
- frozen: {
459
- at: event.occurredAt,
460
- by: typeof event.payload.requestedBy === 'string' ? event.payload.requestedBy : 'unknown',
461
- },
462
- },
463
- wake: {
464
- ...current.wake,
465
- syncedAt: event.ingestedAt,
466
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
467
- },
468
- });
469
- }
470
- if (event.sourceEventType === WORK_ITEM_UNFROZEN_EVENT) {
471
- const nextContext = { ...current.context };
472
- delete nextContext.frozen;
473
- return parseIssueStateRecord({
474
- ...current,
475
- issue: {
476
- ...current.issue,
477
- labels: current.issue.labels.filter((label) => label !== FROZEN_WORK_ITEM_LABEL),
478
- },
479
- context: nextContext,
480
- wake: {
481
- ...current.wake,
482
- syncedAt: event.ingestedAt,
483
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
484
- },
485
- });
486
- }
487
- if (event.sourceEventType === WORKSPACE_CLEANED_EVENT) {
488
- return parseIssueStateRecord({
489
- ...current,
490
- wake: {
491
- ...current.wake,
492
- workspacePath: undefined,
493
- syncedAt: event.ingestedAt,
494
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
495
- },
496
- });
497
- }
498
- if (event.sourceEventType === 'ticket.labels.updated') {
499
- const labels = stringArrayFromPayload(event.payload.labels);
500
- return parseIssueStateRecord({
501
- ...current,
502
- issue: {
503
- ...current.issue,
504
- labels,
505
- },
506
- wake: {
507
- ...current.wake,
508
- expectedEcho: {
509
- ...current.wake.expectedEcho,
510
- labels,
511
- },
512
- syncedAt: event.ingestedAt,
513
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
514
- },
515
- });
516
- }
517
- // Purely an audit record; must never affect projection state, so a full
518
- // event replay produces byte-identical output whether or not this
519
- // synthesized event happens to be included in the batch being folded
520
- // (it's created as a fold side effect, not iterated over on the original
521
- // pass, but is naturally present on a later full replay).
522
- if (event.sourceEventType === CORRELATION_PRIMARY_CONFLICT_EVENT) {
523
- return current;
524
- }
525
- if (event.sourceEventType === CORRELATION_REGISTERED_EVENT) {
526
- const payload = event.payload;
527
- // ADR 0001 §6 is a *downgrade* rule, and only a downgrade rule: "a second
528
- // `primary` registration on a claimed URI is downgraded to `secondary` and
529
- // a warning event appended." It says nothing about promoting a requested
530
- // `secondary`, and §5 lists `relation` as a payload input — so a requested
531
- // `secondary` must stay `secondary`. Folding a requested `secondary` up to
532
- // `primary` would silently rewrite the declaration the event made, and the
533
- // downgrade rule is a corruption guard, not a merge rule — it exists to
534
- // stop a second claim on a URI, not to promote unclaimed ones.
535
- //
536
- // (No shipped caller declares `secondary` yet — `wake correlate` hardcodes
537
- // `primary`. The rule follows the spec's payload contract, which is the
538
- // standard here; an emitter is #82's scope.)
539
- //
540
- // requested primary + held by ANOTHER work item -> secondary + conflict
541
- // requested primary + unclaimed / held by THIS -> primary, indexed
542
- // requested secondary -> secondary, unindexed
543
- //
544
- // A requested-secondary never touching the index is by design, not an
545
- // orphan: the index is primary-only (ADR §5 — the resolver stamps the
546
- // *primary* work item's canonical key), so a secondary has nothing to
547
- // register. Promotion still requires the incumbent to retract first —
548
- // never let a second registration silently steal a uri.
549
- const incumbent = await ctx.resourceIndex.resolve(payload.resourceUri);
550
- const heldByAnotherWorkItem = incumbent !== undefined && incumbent !== current.workItemKey;
551
- const relation = payload.relation === 'primary' && !heldByAnotherWorkItem ? 'primary' : 'secondary';
552
- if (relation === 'primary') {
553
- await ctx.resourceIndex.register(payload.resourceUri, current.workItemKey);
554
- }
555
- else {
556
- if (payload.relation === 'primary') {
557
- await ctx.appendEvent(createEventEnvelope({
558
- eventId: `${event.eventId}-primary-conflict`,
559
- workItemKey: current.workItemKey,
560
- streamScope: 'work-item',
561
- direction: 'internal',
562
- sourceSystem: 'wake',
563
- sourceEventType: CORRELATION_PRIMARY_CONFLICT_EVENT,
564
- sourceRefs: event.sourceRefs,
565
- occurredAt: event.occurredAt,
566
- ingestedAt: event.ingestedAt,
567
- trigger: 'context-only',
568
- payload: {
569
- resourceUri: payload.resourceUri,
570
- incumbentWorkItemKey: incumbent,
571
- },
572
- }));
573
- }
574
- // Coherent inverse of the retraction gate below: a registration that
575
- // folds to non-primary must never leave the index stranded crediting
576
- // this work item. This is reachable as a genuine *self-demotion* — a
577
- // work item that holds a uri primary and then registers it `secondary`
578
- // — where leaving the index pointing at us would contradict our own
579
- // correlatedResources[] entry. Gated on actual index ownership, so the
580
- // ordinary requested-secondary-on-an-unclaimed-uri case (owner
581
- // undefined) correctly leaves the index untouched (finding B).
582
- const owner = await ctx.resourceIndex.resolve(payload.resourceUri);
583
- if (owner === current.workItemKey) {
584
- await ctx.resourceIndex.retract(payload.resourceUri);
585
- }
586
- }
587
- const entry = {
588
- resourceUri: payload.resourceUri,
589
- role: payload.role,
590
- relation,
591
- provenance: payload.provenance,
592
- ...(payload.registeredBy === undefined ? {} : { registeredBy: payload.registeredBy }),
593
- registeredAt: event.occurredAt,
594
- };
595
- const currentCorrelatedResources = current.correlatedResources;
596
- const existingIndex = currentCorrelatedResources.findIndex((resource) => resource.resourceUri === payload.resourceUri);
597
- const correlatedResources = existingIndex === -1
598
- ? [...currentCorrelatedResources, entry]
599
- : currentCorrelatedResources.map((resource, index) => index === existingIndex ? entry : resource);
600
- return parseIssueStateRecord({
601
- ...current,
602
- correlatedResources,
603
- wake: {
604
- ...current.wake,
605
- syncedAt: event.ingestedAt,
606
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
607
- },
608
- });
609
- }
610
- if (event.sourceEventType === CORRELATION_RETRACTED_EVENT) {
611
- const payload = event.payload;
612
- const currentCorrelatedResources = current.correlatedResources;
613
- // Retract whenever the index currently credits this work item —
614
- // regardless of the locally stored relation — so the registration and
615
- // retraction gates are coherent inverses across a demotion. Gating this
616
- // on `existing?.relation === 'primary'` (the locally folded relation at
617
- // *registration* time) can strand the index pointing at a work item that
618
- // no longer holds the resource once that relation has drifted, which is
619
- // exactly the identity-critical path `resolve()` depends on (finding B).
620
- const owner = await ctx.resourceIndex.resolve(payload.resourceUri);
621
- if (owner === current.workItemKey) {
622
- await ctx.resourceIndex.retract(payload.resourceUri);
623
- }
624
- return parseIssueStateRecord({
625
- ...current,
626
- correlatedResources: currentCorrelatedResources.filter((resource) => resource.resourceUri !== payload.resourceUri),
627
- wake: {
628
- ...current.wake,
629
- syncedAt: event.ingestedAt,
630
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
631
- },
632
- });
633
- }
634
- return parseIssueStateRecord({
635
- ...current,
636
- wake: {
637
- ...current.wake,
638
- syncedAt: event.ingestedAt,
639
- recentEventIds: [...current.wake.recentEventIds, event.eventId].slice(-10),
640
- },
641
- });
642
- }
643
- export function createProjectionUpdater(deps) {
644
- const applyEventCtx = {
645
- resourceIndex: deps.resourceIndex,
646
- appendEvent: (event) => deps.stateStore.appendEventEnvelope(event),
647
- };
648
- return {
649
- async rebuildFromEvents(events) {
650
- // Correlation-index-affecting events (wake.correlation.registered in
651
- // particular) must be folded in *globally* ordered time, not grouped by
652
- // workItemKey and folded per-group. resourceIndex is shared/global
653
- // across all work items, so whichever group happens to be visited first
654
- // (Map insertion order — i.e. whichever work item's earliest event
655
- // happens to appear first in the input array) would otherwise decide
656
- // who wins a primary claim, independent of when the registration events
657
- // actually occurred. That makes a full replay (this function, called
658
- // with every event) diverge from the live/incremental fold (this
659
- // function, called with one new event at a time as it happens) for any
660
- // two work items whose creation order and registration order disagree
661
- // — silently handing resumption for a resource to the wrong work item
662
- // after `rm -rf state/` + replay. Sorting once, globally, by
663
- // (ingestedAt, eventId) and folding in that single pass keeps replay
664
- // and live agreeing by construction, for both the index and the
665
- // per-work-item projection fold (a global sort is still a valid order
666
- // for each individual work item's own events, since it's a stable
667
- // sort over a total order that's consistent with each event's own
668
- // ingestedAt).
669
- // Sort is stable (ES2019+), so events sharing an ingestedAt keep their
670
- // input order — which is append/arrival order, the same order the live
671
- // fold saw them in. Do not add a tie-break on eventId: ids are not
672
- // chronological, so that would reorder same-timestamp events within a
673
- // work item against the order they actually happened.
674
- const ordered = [...events].sort((left, right) => left.ingestedAt.localeCompare(right.ingestedAt));
675
- const projections = new Map();
676
- const touchedWorkItemKeys = [];
677
- for (const event of ordered) {
678
- if (!projections.has(event.workItemKey)) {
679
- touchedWorkItemKeys.push(event.workItemKey);
680
- projections.set(event.workItemKey, await deps.stateStore.readIssueState(event.workItemKey));
681
- }
682
- const current = projections.get(event.workItemKey) ?? null;
683
- projections.set(event.workItemKey, await applyEvent(current, event, applyEventCtx, deps.config));
684
- }
685
- for (const workItemKey of touchedWorkItemKeys) {
686
- const projection = projections.get(workItemKey) ?? null;
687
- if (projection !== null) {
688
- await deps.stateStore.writeIssueState(projection);
689
- }
690
- }
691
- },
692
- };
693
- }