@jr2/orchestrator 0.4.1 → 0.6.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 (361) hide show
  1. package/README.md +20 -2
  2. package/console/components/app.ts +2 -0
  3. package/console/components/fleet.ts +14 -1
  4. package/console/components/machine-pane.ts +41 -2
  5. package/console/main.ts +13 -1
  6. package/console/store.ts +102 -6
  7. package/console/style.css +21 -0
  8. package/docs/agents-block.md +5 -0
  9. package/docs/concepts/agents-and-turns.md +478 -0
  10. package/docs/concepts/architecture.md +243 -0
  11. package/docs/concepts/charts.md +146 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +257 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +658 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +298 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +306 -0
  20. package/docs/concepts/the-menu.md +381 -0
  21. package/docs/concepts/workspaces.md +756 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +383 -0
  24. package/docs/get-started/connect-a-model.md +248 -0
  25. package/docs/get-started/create-an-instance.md +194 -0
  26. package/docs/get-started/have-your-assistant-write-a-machine.md +993 -0
  27. package/docs/get-started/install.md +130 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +308 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +224 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/cut-a-worktree.md +560 -0
  34. package/docs/guides/fix-a-failed-converge.md +361 -0
  35. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  36. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  37. package/docs/guides/let-an-agent-write-a-chart.md +230 -0
  38. package/docs/guides/look-inside-a-sandbox.md +177 -0
  39. package/docs/guides/mirror-the-kit-images.md +176 -0
  40. package/docs/guides/move-from-kind-to-a-shared-cluster.md +318 -0
  41. package/docs/guides/open-the-console.md +91 -0
  42. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  43. package/docs/guides/push-and-sign-a-branch.md +532 -0
  44. package/docs/guides/run-a-chart-by-name.md +270 -0
  45. package/docs/guides/run-a-command-or-call-a-url.md +587 -0
  46. package/docs/guides/run-one-worker-per-item.md +322 -0
  47. package/docs/guides/set-an-agents-access-and-dials.md +420 -0
  48. package/docs/guides/size-a-workspace.md +217 -0
  49. package/docs/guides/start-a-run-from-a-run.md +237 -0
  50. package/docs/guides/start-a-run.md +207 -0
  51. package/docs/guides/upgrade-the-kit-version.md +240 -0
  52. package/docs/guides/use-a-hosted-provider.md +214 -0
  53. package/docs/guides/use-a-local-model.md +313 -0
  54. package/docs/guides/watch-a-run.md +349 -0
  55. package/docs/guides/write-a-workflow.md +318 -0
  56. package/docs/index.md +44 -0
  57. package/docs/llms.txt +237 -0
  58. package/docs/reference/authoring-surface/agent.md +151 -0
  59. package/docs/reference/authoring-surface/answer.md +156 -0
  60. package/docs/reference/authoring-surface/arg.md +82 -0
  61. package/docs/reference/authoring-surface/basic.md +62 -0
  62. package/docs/reference/authoring-surface/bearer.md +53 -0
  63. package/docs/reference/authoring-surface/branchname.md +15 -0
  64. package/docs/reference/authoring-surface/chartschema.md +23 -0
  65. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  66. package/docs/reference/authoring-surface/command.md +206 -0
  67. package/docs/reference/authoring-surface/customize.md +64 -0
  68. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  69. package/docs/reference/authoring-surface/defineevent.md +114 -0
  70. package/docs/reference/authoring-surface/dispatch.md +126 -0
  71. package/docs/reference/authoring-surface/fail.md +96 -0
  72. package/docs/reference/authoring-surface/file.md +142 -0
  73. package/docs/reference/authoring-surface/frompick.md +277 -0
  74. package/docs/reference/authoring-surface/http.md +259 -0
  75. package/docs/reference/authoring-surface/isfailure.md +58 -0
  76. package/docs/reference/authoring-surface/isready.md +14 -0
  77. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  78. package/docs/reference/authoring-surface/open.md +105 -0
  79. package/docs/reference/authoring-surface/openbody.md +86 -0
  80. package/docs/reference/authoring-surface/paramsof.md +143 -0
  81. package/docs/reference/authoring-surface/pool.md +186 -0
  82. package/docs/reference/authoring-surface/push.md +130 -0
  83. package/docs/reference/authoring-surface/readyof.md +21 -0
  84. package/docs/reference/authoring-surface/secret.md +139 -0
  85. package/docs/reference/authoring-surface/sentby.md +83 -0
  86. package/docs/reference/authoring-surface/source.md +51 -0
  87. package/docs/reference/authoring-surface/text.md +62 -0
  88. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  89. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  90. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  91. package/docs/reference/authoring-surface/type-agentslots.md +15 -0
  92. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  93. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  94. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  95. package/docs/reference/authoring-surface/type-arg.md +19 -0
  96. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  97. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  98. package/docs/reference/authoring-surface/type-binding.md +61 -0
  99. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  100. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  101. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  102. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  103. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  104. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  105. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  106. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  107. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  108. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  109. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  110. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  111. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  112. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  113. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  114. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  115. package/docs/reference/authoring-surface/type-childat.md +12 -0
  116. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  117. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  118. package/docs/reference/authoring-surface/type-commandinput.md +20 -0
  119. package/docs/reference/authoring-surface/type-commandoptions.md +56 -0
  120. package/docs/reference/authoring-surface/type-commandoutput.md +122 -0
  121. package/docs/reference/authoring-surface/type-customize.md +111 -0
  122. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  123. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  124. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  125. package/docs/reference/authoring-surface/type-dispatchoutput.md +72 -0
  126. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  127. package/docs/reference/authoring-surface/type-duration.md +15 -0
  128. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  129. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  130. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  131. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  132. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  133. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  134. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  135. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  136. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  137. package/docs/reference/authoring-surface/type-failure.md +19 -0
  138. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  139. package/docs/reference/authoring-surface/type-filebody.md +37 -0
  140. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  141. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  142. package/docs/reference/authoring-surface/type-hostinjectedinput.md +56 -0
  143. package/docs/reference/authoring-surface/type-httpdefinition.md +104 -0
  144. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  145. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  146. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  147. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +95 -0
  149. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  150. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  151. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  152. package/docs/reference/authoring-surface/type-lends.md +38 -0
  153. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  154. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  155. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  156. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  157. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  158. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +27 -0
  159. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  160. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  161. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  162. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  163. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  164. package/docs/reference/authoring-surface/type-opencommandinput.md +22 -0
  165. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  166. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  167. package/docs/reference/authoring-surface/type-openof.md +12 -0
  168. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  169. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  170. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +26 -0
  171. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +27 -0
  172. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  173. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  174. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  175. package/docs/reference/authoring-surface/type-pushinput.md +34 -0
  176. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  177. package/docs/reference/authoring-surface/type-pushoutput.md +149 -0
  178. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  179. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  180. package/docs/reference/authoring-surface/type-reached.md +12 -0
  181. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  182. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  183. package/docs/reference/authoring-surface/type-sandboxoptions.md +84 -0
  184. package/docs/reference/authoring-surface/type-secret.md +25 -0
  185. package/docs/reference/authoring-surface/type-sender.md +19 -0
  186. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  187. package/docs/reference/authoring-surface/type-size.md +85 -0
  188. package/docs/reference/authoring-surface/type-slothandles.md +24 -0
  189. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  190. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  191. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  192. package/docs/reference/authoring-surface/type-text.md +19 -0
  193. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  194. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  195. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  196. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  197. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  198. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  199. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  200. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  201. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  202. package/docs/reference/authoring-surface/type-workspaced.md +115 -0
  203. package/docs/reference/authoring-surface/type-workspacehandles.md +22 -0
  204. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  205. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  206. package/docs/reference/authoring-surface/type-workspaceoptions.md +27 -0
  207. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  208. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  209. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  210. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  211. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  212. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  213. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  214. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  215. package/docs/reference/authoring-surface/workspace.md +287 -0
  216. package/docs/reference/authoring-surface/worktree.md +71 -0
  217. package/docs/reference/authoring-surface.md +217 -0
  218. package/docs/reference/cli.md +433 -0
  219. package/docs/reference/config.md +765 -0
  220. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +114 -0
  221. package/docs/reference/decisions/0002-duplex-streaming-actor.md +117 -0
  222. package/docs/reference/decisions/0004-worktree-provisioning-model.md +130 -0
  223. package/docs/reference/decisions/0005-sandbox-pod-composition.md +240 -0
  224. package/docs/reference/decisions/0006-agent-control-surface.md +50 -0
  225. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  226. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  227. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  228. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  229. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  230. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  231. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +175 -0
  232. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  233. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +271 -0
  234. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +126 -0
  235. package/docs/reference/decisions/0017-pool-over-a-source-port.md +71 -0
  236. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  237. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +287 -0
  238. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  239. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +101 -0
  240. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  241. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +175 -0
  242. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +436 -0
  243. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  244. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  245. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +245 -0
  246. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +90 -0
  247. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  248. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  249. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +193 -0
  250. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  251. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +126 -0
  252. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  253. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  254. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  255. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  256. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  257. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  258. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  259. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  260. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  261. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  262. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  263. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  264. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  265. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  266. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  267. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  268. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +238 -0
  269. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +86 -0
  270. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +123 -0
  271. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  272. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  273. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  274. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  275. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +108 -0
  276. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +427 -0
  277. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  278. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  279. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +239 -0
  280. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +82 -0
  281. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  282. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  283. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +552 -0
  284. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +458 -0
  285. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +475 -0
  286. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  287. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +334 -0
  288. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  289. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  290. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  291. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  292. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
  293. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
  294. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  295. package/docs/reference/decisions.md +86 -0
  296. package/docs/reference/glossary.md +687 -0
  297. package/docs/reference/http-api.md +1386 -0
  298. package/docs/reference/machines.md +189 -0
  299. package/docs/reference/mechanism-events.md +72 -0
  300. package/docs/why.md +207 -0
  301. package/package.json +5 -3
  302. package/src/actor.ts +694 -106
  303. package/src/agent.ts +114 -38
  304. package/src/ambient.ts +3 -4
  305. package/src/assembly-checks.ts +189 -7
  306. package/src/cause.ts +234 -0
  307. package/src/chart.ts +345 -46
  308. package/src/charts.ts +43 -8
  309. package/src/config.ts +440 -178
  310. package/src/custodian.lua +17 -5
  311. package/src/custodian.ts +325 -99
  312. package/src/customize.ts +199 -49
  313. package/src/dispatch-effect.ts +945 -0
  314. package/src/door-compat.ts +217 -0
  315. package/src/durability.ts +11 -4
  316. package/src/effect-checks.ts +50 -3
  317. package/src/effect.ts +641 -128
  318. package/src/effector-wire.ts +165 -13
  319. package/src/fail.ts +222 -0
  320. package/src/harness-client.ts +587 -50
  321. package/src/harness-env.ts +33 -0
  322. package/src/held-secrets.ts +125 -16
  323. package/src/holds.ts +3 -0
  324. package/src/http-effect.ts +556 -58
  325. package/src/http.ts +480 -30
  326. package/src/index.ts +74 -82
  327. package/src/instance.ts +68 -8
  328. package/src/internal.ts +85 -0
  329. package/src/kube-client.ts +6 -3
  330. package/src/machine-doc.ts +24 -7
  331. package/src/mechanism-routes.ts +168 -0
  332. package/src/model-keys.generated.ts +53 -0
  333. package/src/model-keys.ts +42 -58
  334. package/src/names.ts +19 -6
  335. package/src/notices.ts +483 -41
  336. package/src/open-body.ts +242 -40
  337. package/src/open-effect.ts +81 -8
  338. package/src/open.ts +90 -6
  339. package/src/parts.ts +213 -30
  340. package/src/party.ts +101 -2
  341. package/src/pick.ts +497 -11
  342. package/src/pool.ts +282 -50
  343. package/src/registration.ts +133 -12
  344. package/src/repo-identity.ts +7 -3
  345. package/src/repos.ts +22 -3
  346. package/src/run-host.ts +665 -45
  347. package/src/run-output.ts +38 -0
  348. package/src/sandbox-effect.ts +457 -71
  349. package/src/sandbox-kube.ts +66 -17
  350. package/src/secret-placement.ts +59 -17
  351. package/src/server.ts +3 -0
  352. package/src/setup.ts +325 -62
  353. package/src/size.ts +121 -9
  354. package/src/snapshot-store.ts +54 -10
  355. package/src/stub-harness.ts +44 -16
  356. package/src/timers.ts +14 -1
  357. package/src/tokens.ts +107 -32
  358. package/src/vocabulary.ts +88 -14
  359. package/src/wire.ts +100 -26
  360. package/src/workspace.ts +771 -238
  361. package/src/worktree.ts +249 -0
package/src/pool.ts CHANGED
@@ -18,6 +18,8 @@
18
18
  // final, distinct status (jr exit 2), never an indistinguishable idle poll;
19
19
  // - waiting — children parked (gates, agents) → the run stays open; the gates list is the
20
20
  // "what needs me" surface (jr exit 3).
21
+ // With failures and `failures: "fail"` (the default), a drained or deadlocked pool fails the run instead
22
+ // (ADR-0073), and the detail of the Failure carries the status.
21
23
  // The pool can make this call because it owns both the source and the children. Distinguishing
22
24
  // drained from deadlocked needs one more bit than `null` carries, so a Source's `next` MAY
23
25
  // resolve the richer `{ item: null, open: n }` shape; a bare `null` means drained-when-idle.
@@ -33,67 +35,143 @@ import {
33
35
  } from "xstate";
34
36
  import type { z } from "zod";
35
37
  import type { EventDef } from "@jr2/agent-protocol";
38
+ import { fail, failurePayload, isFailure, type FailureInput } from "./fail.ts";
36
39
  import { gate } from "./gate.ts";
37
40
  import { attachWrapperBody, type JR2Wrapper, type WrapperActors } from "./parts.ts";
41
+ import { parsedNestedDoor } from "./vocabulary.ts";
38
42
  import { attachInputSchema, attachVocabulary } from "./vocabulary.ts";
39
43
 
44
+ // The rich shape's `open` is the one bit that separates DRAINED from DEADLOCKED when the pool goes
45
+ // idle (ADR-0017); a bare `null` means drained-when-idle.
40
46
  /**
41
- * What `next` resolves. A plain item claims it; `null` says "nothing ready now" (drained, when
42
- * nothing is running); the rich shape adds `open` — how many items exist but are not ready —
43
- * which is the one bit that separates DRAINED from DEADLOCKED when the pool goes idle.
47
+ * What the `next` of a Source resolves to: an item, to claim it; `null`, when no item is ready
48
+ * now; or `{ item, open }`, where `open` is the number of items that exist but are not ready.
49
+ * When no worker runs and `next` gives no item, the Pool ends: `drained` when `open` is 0 or
50
+ * absent, `deadlocked` when `open` is more than 0.
44
51
  */
45
52
  export type SourceNextResult<T> = T | null | { item: T | null; open: number };
46
53
 
47
- /** The Source port (ADR-0017): where a pool draws work from. Build one with {@link source}. */
48
- export type SourceSpec<T> = {
49
- /** Claim the next ready item, excluding the ones already being worked (`input.active`).
50
- * A union of the accepted output shapes because xstate's actor-logic type is invariant in its
51
- * output — a `fromPromise` inferred as `T | null` must still assign. */
54
+ /** The input of a Source's `next`: the ids of the items that workers have now, the ids of the items
55
+ * that settled, and the Pool's input. So a Source over a list that a Machine holds needs no state
56
+ * of its own. */
57
+ export type PoolNextInput<TInput = unknown> = { active: string[]; settled: string[]; input: TInput };
58
+
59
+ // The Source port (ADR-0017).
60
+ /** Where a Pool gets its work items: the Source. Make one with {@link source}. */
61
+ export type SourceSpec<T, TInput = unknown> = {
62
+ // A union of the accepted output shapes because xstate's actor-logic type is invariant in its
63
+ // output — a `fromPromise` inferred as `T | null` must still assign.
64
+ /** Claim the next ready item. Write it as an xstate promise actor,
65
+ * `fromPromise(async ({ input }) => …)`. Its input is {@link PoolNextInput}: `active`, the ids of
66
+ * the items that workers have now, `settled`, the ids of the items that finished or failed, and
67
+ * `input`, the Pool's input. It must not give again an item in `active` or `settled`. It resolves
68
+ * to a {@link SourceNextResult}. The Pool calls it when the run starts, after each claim, and when a worker settles. While workers run and no item is ready, it
69
+ * also calls it on `wake` and every `pollEvery`. */
52
70
  next:
53
- | PromiseActorLogic<SourceNextResult<T>, { active: string[] }>
54
- | PromiseActorLogic<{ item: T | null; open: number }, { active: string[] }>
55
- | PromiseActorLogic<T | null, { active: string[] }>
56
- | PromiseActorLogic<null, { active: string[] }>;
57
- /** An external event def that wakes discovery early (webhook / `jr2 send` push seam). The pool
58
- * serves it as a standing gate named "source"; the def IS the pool machine's vocabulary. */
71
+ | PromiseActorLogic<SourceNextResult<T>, PoolNextInput<TInput>>
72
+ | PromiseActorLogic<{ item: T | null; open: number }, PoolNextInput<TInput>>
73
+ | PromiseActorLogic<T | null, PoolNextInput<TInput>>
74
+ | PromiseActorLogic<null, PoolNextInput<TInput>>;
75
+ // The webhook / `jr2 send` push seam. The def IS the pool machine's vocabulary (ADR-0011).
76
+ /** An external event, made with `defineEvent`, that tells the Pool that new work is ready, for
77
+ * example from a webhook or `jr2 send`. While workers run and no item is ready, it makes the Pool
78
+ * call `next` at once. The Pool serves it as a Gate named `source` for the whole run
79
+ * (`<path>.source` when the Pool is composed in another Machine). Default: none. */
59
80
  wake?: EventDef;
60
- /** Re-query cadence while parked, in ms — for sets that mutate underneath us. */
81
+ // For sets that mutate underneath us.
82
+ /** While workers run and no item is ready, call `next` again after this many milliseconds. Use
83
+ * it for a Source whose items change while the run waits. Default: none, so the Pool calls
84
+ * `next` again only when a worker settles or `wake` comes. */
61
85
  pollEvery?: number;
62
86
  };
63
87
 
64
- /** Declare a Source. Identity by design — the value IS the spec; this is the naming/typing seam. */
65
- export function source<T>(spec: SourceSpec<T>): SourceSpec<T> {
88
+ // Identity by design — the value IS the spec; this is the naming/typing seam.
89
+ /**
90
+ * Declare a Source: where a `pool()` gets its work items, one at a time. It gives back the spec
91
+ * that you pass. The item type `T` types what `next` gives. `pool()` does not carry `T` to
92
+ * `itemId` and `itemInput`, so write the item type on their parameter, as the `pool()` example does.
93
+ *
94
+ * @param spec - How to claim the next item, and when to look again. See {@link SourceSpec}.
95
+ * @returns The same spec.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * import { fromPromise } from "xstate";
100
+ * import { source, type PoolNextInput } from "@jr2/orchestrator";
101
+ *
102
+ * type Issue = { id: string; title: string };
103
+ *
104
+ * const backlog: Issue[] = [
105
+ * { id: "ISSUE-1", title: "Fix the greeting." },
106
+ * { id: "ISSUE-2", title: "Add a --name flag." },
107
+ * ];
108
+ *
109
+ * export const issues = source<Issue>({
110
+ * next: fromPromise(async ({ input }: { input: PoolNextInput }) => {
111
+ * const issue = backlog.find((i) => !input.active.includes(i.id)) ?? null;
112
+ * if (issue) backlog.splice(backlog.indexOf(issue), 1);
113
+ * return issue;
114
+ * }),
115
+ * pollEvery: 60_000,
116
+ * });
117
+ * ```
118
+ *
119
+ * @see docs/guides/run-one-worker-per-item.md
120
+ * @see docs/concepts/pools-and-sources.md
121
+ * @see docs/reference/decisions/0017-pool-over-a-source-port.md
122
+ */
123
+ export function source<T, TInput = unknown>(spec: SourceSpec<T, TInput>): SourceSpec<T, TInput> {
66
124
  return spec;
67
125
  }
68
126
 
69
- /** How a pool run ends (its machine output). `items` maps itemId → the worker's output. */
70
- export type PoolOutput = { status: "drained" | "deadlocked"; items: Record<string, unknown> };
127
+ /** How a pool run ends (its machine output). `items` maps itemId → the output of a worker that got
128
+ * to a final state. `failures` maps itemId → the Failure of a worker that failed (ADR-0073). */
129
+ export type PoolOutput = {
130
+ status: "drained" | "deadlocked";
131
+ items: Record<string, unknown>;
132
+ failures: Record<string, FailureInput>;
133
+ };
71
134
 
72
- /**
73
- * How a pool is configured (ADR-0017, ADR-0033). `TInput` is what came through the pool's door:
74
- * the PARSED schema when `input` declares one, and `unknown` when it does not — absence is
75
- * permissive, so jr2 knows nothing and says so, exactly as `workspace()`'s door-less path does.
76
- * A pool that is fed by something other than a caller may state what it is fed by annotating the
77
- * mapper's parameter; for anything a caller starts, the honest fix is to declare `input`.
78
- */
135
+ // How a pool is configured (ADR-0017, ADR-0033). `TInput` is what came through the pool's door:
136
+ // the PARSED schema when `input` declares one, and `unknown` when it does not — absence is
137
+ // permissive, so jr2 knows nothing and says so, exactly as `workspace()`'s door-less path does.
138
+ // A pool that is fed by something other than a caller may state what it is fed by annotating the
139
+ // mapper's parameter; for anything a caller starts, the honest fix is to declare `input`.
140
+ /** What you pass to `pool()`: where the items come from, how many workers run at once, and what
141
+ * each worker gets. */
79
142
  export type PoolSpec<T, TInput = unknown> = {
80
- /** The machine id (the workflow's name in the Console). Default "pool". */
143
+ /** The Machine id: the name of the Pool in the Console. Default: `"pool"`. */
81
144
  id?: string;
82
- /** The pool's OWN declared run input (ADR-0033) — what `cap` and `itemInput` read off the door,
83
- * and what types their `input`. The WORKER's schema is deliberately not the door: workers are
84
- * fed per-ITEM (the source item, or `itemInput`'s result), never the run body, so it describes
85
- * nothing a starter sends. */
145
+ // The WORKER's schema is deliberately not the door (ADR-0033): workers are fed per-ITEM (the
146
+ // source item, or `itemInput`'s result), never the run body, so it describes nothing a starter
147
+ // sends.
148
+ /** The run input of the Pool, as a zod object: what a caller sends to start a run. `cap` and
149
+ * `itemInput` read it, typed by it. A worker never gets it: each worker gets its item. Default:
150
+ * none, and then their `input` is `unknown`. */
86
151
  input?: z.ZodObject;
87
- source: SourceSpec<T>;
88
- /** Stable child identity: the spawn id, the active-set entry, the outcome key. */
152
+ // `SourceSpec<T>` too: xstate's actor logic is invariant in its input, so a `next` that does not
153
+ // read the Pool's input must still assign under a typed door.
154
+ /** Where the items come from. Make it with {@link source}. */
155
+ source: SourceSpec<T, TInput> | SourceSpec<T>;
156
+ // Stable child identity: the spawn id, the active-set entry, the outcome key.
157
+ /** The id of an item. The Pool uses it as the id of the item's worker and in the `active` list
158
+ * that `next` gets. It is also the key of that worker's output in `items`. Give each item an id
159
+ * of its own, and the same id every time. */
89
160
  itemId: (item: T) => string;
90
- /** Max concurrent workers — a number, or derived from run input. Default 1. */
161
+ /** The most workers that run at once: a number, or a function of the run input. Default: `1`. */
91
162
  cap?: number | ((args: { input: TInput }) => number);
92
- /** Map an item (+ run input) to the worker's input. Default: the item itself. */
163
+ /** Make a worker's input from its item and the run input. Default: the item itself. */
93
164
  itemInput?: (item: T, args: { input: TInput }) => unknown;
94
- /** Terminal policy when the source runs dry and children settle. Only "final" exists today
95
- * (jr semantics: runs END); a parking policy can be added when a workflow needs one. */
165
+ // jr semantics: runs END; a parking policy can be added when a workflow needs one.
166
+ /** What the Pool does when the Source has no more items and every worker has settled. Only
167
+ * `"final"`: the run ends. Default: `"final"`. */
96
168
  onDrained?: "final";
169
+ /** What the Pool does at the end when one or more workers failed. The Pool catches each worker's
170
+ * Failure either way, so one failed item does not stop the other workers. `"fail"`: when the Pool
171
+ * ends with failures, `drained` or `deadlocked`, it fails the run with the reason
172
+ * `"<n> of <m> items failed"` and the detail `{ status, items, failures }`. `"report"`: the Pool
173
+ * ends `done`, with the failures in its output. Default: `"fail"`. */
174
+ failures?: "fail" | "report";
97
175
  };
98
176
 
99
177
  /**
@@ -102,12 +180,14 @@ export type PoolSpec<T, TInput = unknown> = {
102
180
  * TRANSPARENT to its worker (ADR-0049), so `customize(machine, { agents })` on a pool-rooted
103
181
  * workflow offers the WORKER's Agents and the composer never spells `worker`. {@link JR2Wrapper} is
104
182
  * what marks it a jr2 wrapper: the transparency follows the marker, not the slot's spelling.
183
+ *
184
+ * @internal
105
185
  */
106
186
  export type PoolMachine<TWorker extends AnyStateMachine, TInput> = StateMachine<
107
187
  any,
108
188
  any,
109
189
  any,
110
- WrapperActors<"worker", TWorker, "next" | "gate">,
190
+ WrapperActors<"worker", TWorker, "next" | "gate" | "fail">,
111
191
  any,
112
192
  any,
113
193
  any,
@@ -128,6 +208,8 @@ type PoolCtx = {
128
208
  active: string[];
129
209
  /** Settled workers' outputs, by item id — the pool's output collects them. */
130
210
  items: Record<string, unknown>;
211
+ /** Failed workers' payloads, by item id (ADR-0073). */
212
+ failures: Record<string, FailureInput>;
131
213
  status?: "drained" | "deadlocked";
132
214
  };
133
215
 
@@ -146,15 +228,90 @@ function normalizeClaim(output: unknown): Claim {
146
228
  return { item: output, open: 0 };
147
229
  }
148
230
 
231
+ // Build the pool machine (ADR-0017). Returns a plain machine — the usual workflow ROOT, but
232
+ // nestable as a child like any other. Its vocabulary is the wake def alone: the worker keeps its
233
+ // own (ADR-0011, ADR-0049).
149
234
  /**
150
- * Build the pool machine (ADR-0017). Returns a plain machine — the usual workflow ROOT
151
- * (`export const machine = pool(...)`), but nestable as a child like any other. Its vocabulary is
152
- * the wake def alone: the worker keeps its own (ADR-0011, ADR-0049).
235
+ * Make a Pool: a Machine that runs one worker Machine for each item that a Source gives, with at
236
+ * most `cap` workers at once. Use it as the root of a Workflow (`export const machine = pool(…)`),
237
+ * or compose it in another Machine.
238
+ *
239
+ * The run ends when the Source has no item ready and no worker runs. Its output is
240
+ * `{ status, items, failures }`: `status` is `"drained"` when no item is left, or `"deadlocked"`
241
+ * when items exist but none can start; `items` holds the output of each worker that got to a final
242
+ * state, by item id; `failures` holds the Failure of each worker that failed, by item id. A worker's
243
+ * Failure stops only that worker: the other workers and their Sandboxes stay. When the Pool ends
244
+ * with one or more failures, `drained` or `deadlocked`, it fails the run by default: the reason is
245
+ * `"<n> of <m> items failed"`, and the detail is `{ status, items, failures }`. With
246
+ * `failures: "report"`, it ends `done` with that output. A worker's defect ends the whole run
247
+ * `error`. While workers wait on Gates, the run stays open.
248
+ *
249
+ * `customize()` reaches through a Pool to its worker: `customize(pool, { agents })` retunes the
250
+ * worker's Agents. The events that the Pool accepts are its `wake` event only; each worker keeps
251
+ * its own.
252
+ *
253
+ * @param worker - The Machine to run for each item. It gets the item, or what `itemInput` makes
254
+ * of it, as its input. It does not get the run input.
255
+ * @param spec - The Source, the item id, the cap, and the worker's input. See {@link PoolSpec}.
256
+ * @returns The Pool, as a Machine.
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * import { fromPromise } from "xstate";
261
+ * import { customize, pool, source, type PoolNextInput } from "@jr2/orchestrator";
262
+ * import { task } from "@jr2/machines";
263
+ *
264
+ * type Issue = { id: string; title: string };
265
+ *
266
+ * const backlog: Issue[] = [
267
+ * { id: "ISSUE-1", title: "Fix the greeting." },
268
+ * { id: "ISSUE-2", title: "Add a --name flag." },
269
+ * ];
270
+ *
271
+ * const issues = source<Issue>({
272
+ * next: fromPromise(async ({ input }: { input: PoolNextInput }) => {
273
+ * const issue = backlog.find((i) => !input.active.includes(i.id)) ?? null;
274
+ * if (issue) backlog.splice(backlog.indexOf(issue), 1);
275
+ * return issue;
276
+ * }),
277
+ * });
278
+ *
279
+ * const fixer = customize(task, {
280
+ * repos: { app: { url: "https://github.com/you/app.git" } },
281
+ * agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
282
+ * });
283
+ *
284
+ * export const machine = pool(fixer, {
285
+ * id: "issues",
286
+ * source: issues,
287
+ * itemId: (issue: Issue) => issue.id,
288
+ * cap: 2,
289
+ * itemInput: (issue: Issue) => ({ prompt: issue.title, branch: `fix/${issue.id}` }),
290
+ * });
291
+ * ```
292
+ *
293
+ * @see docs/guides/run-one-worker-per-item.md
294
+ * @see docs/concepts/pools-and-sources.md
295
+ * @see docs/reference/decisions/0017-pool-over-a-source-port.md
153
296
  */
154
297
  export function pool<TWorker extends AnyStateMachine, TSchema extends z.ZodObject>(
155
298
  worker: TWorker,
156
299
  spec: PoolSpec<any, z.infer<TSchema>> & { input: TSchema },
157
300
  ): PoolMachine<TWorker, z.infer<TSchema>>;
301
+ /**
302
+ * Make a Pool with no `input` schema. A Pool is a Machine that runs one worker Machine for each
303
+ * item that a Source gives, with at most `cap` workers at once. See {@link pool} for the full description and
304
+ * an example.
305
+ *
306
+ * @param worker - The Machine to run for each item. It gets the item, or what `itemInput` makes
307
+ * of it, as its input. It does not get the run input.
308
+ * @param spec - The Source, the item id, the cap, and the worker's input. See {@link PoolSpec}.
309
+ * @returns The Pool, as a Machine.
310
+ *
311
+ * @see docs/guides/run-one-worker-per-item.md
312
+ * @see docs/concepts/pools-and-sources.md
313
+ * @see docs/reference/decisions/0017-pool-over-a-source-port.md
314
+ */
158
315
  export function pool<TWorker extends AnyStateMachine, TInput = unknown>(
159
316
  worker: TWorker,
160
317
  spec: PoolSpec<any, TInput> & { input?: never },
@@ -166,17 +323,32 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
166
323
  /** `xstate.done.actor.<childId>` → the settled worker's item id. */
167
324
  const doneChildOf = (event: unknown): string =>
168
325
  ((event as { type: string }).type.match(/^xstate\.done\.actor\.(.+)$/) as RegExpMatchArray)[1]!;
326
+ /** `xstate.error.actor.<childId>` → the failed worker's item id. */
327
+ const errorChildOf = (event: unknown): string =>
328
+ ((event as { type: string }).type.match(/^xstate\.error\.actor\.(.+)$/) as RegExpMatchArray)[1]!;
169
329
 
330
+ const report = spec.failures === "report";
331
+ const failuresOf = (context: unknown) => (context as PoolCtx).failures;
170
332
  const machine = setup({
171
- actors: { worker, next: spec.source.next, gate },
333
+ actors: { worker, next: spec.source.next, gate, fail: fail() },
172
334
  }).createMachine({
173
335
  id: spec.id ?? "pool",
174
- context: ({ input }): PoolCtx => ({
175
- runInput: (input ?? {}) as Record<string, unknown>,
176
- cap: typeof spec.cap === "function" ? spec.cap({ input }) : (spec.cap ?? 1),
177
- active: [],
178
- items: {},
179
- }),
336
+ context: ({ input, self }): PoolCtx => {
337
+ // RunHost parsed a root run's door. A parent's input is parsed here, and a value that is
338
+ // wrong starts no worker (ADR-0033). The handles and `instanceId` are no caller's to send.
339
+ const sent = (input ?? {}) as Record<string, unknown>;
340
+ const runInput =
341
+ spec.input && self._parent
342
+ ? parsedNestedDoor(spec.input, sent, ["workspace", "instanceId"], "pool input invalid, before any worker")
343
+ : sent;
344
+ return {
345
+ runInput,
346
+ cap: typeof spec.cap === "function" ? spec.cap({ input: runInput as never }) : (spec.cap ?? 1),
347
+ active: [],
348
+ items: {},
349
+ failures: {},
350
+ };
351
+ },
180
352
  // The wake seam: a standing gate for the whole run's life (root invokes stop only at final).
181
353
  // The invoke id IS the gate's name (the id derives from the actor path — ADR-0011):
182
354
  // `source` when the pool is the root, `<path>.source` nested — so two nested
@@ -201,6 +373,28 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
201
373
  stopChild(({ event }) => doneChildOf(event)),
202
374
  ],
203
375
  },
376
+ // A worker failed (ADR-0073): keep its Failure, free its slot, stop the ref, and go look for
377
+ // more work. The Pool is the parent that catches it, so one failed item does not end the run
378
+ // and the host does not delete the Sandboxes of the other workers — a worker parked at a Gate
379
+ // keeps its work (parking is retention, ADR-0012). Only a Failure of a live worker: a defect,
380
+ // or an error of `next`, takes no transition here, so it goes up and ends the run `error`.
381
+ "xstate.error.actor.*": {
382
+ guard: ({ context, event }) =>
383
+ isFailure((event as { error?: unknown }).error) &&
384
+ (context as unknown as PoolCtx).active.includes(errorChildOf(event)),
385
+ target: ".discovering",
386
+ actions: [
387
+ assign({
388
+ failures: ({ context, event }) => ({
389
+ ...(context as unknown as PoolCtx).failures,
390
+ [errorChildOf(event)]: failurePayload((event as unknown as { error: never }).error),
391
+ }),
392
+ active: ({ context, event }) =>
393
+ (context as unknown as PoolCtx).active.filter((id) => id !== errorChildOf(event)),
394
+ }),
395
+ stopChild(({ event }) => errorChildOf(event)),
396
+ ],
397
+ },
204
398
  },
205
399
  states: {
206
400
  discovering: {
@@ -209,7 +403,11 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
209
403
  invoke: {
210
404
  id: "discover",
211
405
  src: "next",
212
- input: ({ context }) => ({ active: (context as PoolCtx).active }),
406
+ input: ({ context }) => ({
407
+ active: (context as PoolCtx).active,
408
+ settled: [...Object.keys((context as PoolCtx).items), ...Object.keys((context as PoolCtx).failures)],
409
+ input: (context as PoolCtx).runInput,
410
+ }),
213
411
  onDone: [
214
412
  {
215
413
  // Claimed: record it, spawn its worker (TOP-LEVEL spawnChild — the visualize
@@ -234,6 +432,22 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
234
432
  // Nothing ready, workers live → healthy parking: their settling re-queries, as do
235
433
  // the wake gate and the poll timer. This is jr's exit-3 "waiting" as a state.
236
434
  { guard: ({ context }) => (context as PoolCtx).active.length > 0, target: "parked" },
435
+ // Idle with failures: the run fails (ADR-0017, ADR-0073), at a drain and at a deadlock.
436
+ // No worker is live, so the Failure deletes no sibling's Sandbox. `"report"` has no
437
+ // `failing` state and no branch to it, so the walk lists no state that fails the run. The
438
+ // branch is spread in, so xstate cannot infer the actor type of its action: `as never`
439
+ // lets the check pass.
440
+ ...(report
441
+ ? []
442
+ : [
443
+ {
444
+ guard: ({ context }: { context: unknown }) => Object.keys(failuresOf(context)).length > 0,
445
+ target: "failing",
446
+ actions: assign({
447
+ status: ({ event }) => (claimOf(event).open > 0 ? ("deadlocked" as const) : ("drained" as const)),
448
+ }) as never,
449
+ },
450
+ ]),
237
451
  // Idle + items exist that will never become ready by themselves → jr's exit 2.
238
452
  {
239
453
  guard: ({ event }) => claimOf(event).open > 0,
@@ -246,13 +460,30 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
246
460
  },
247
461
  },
248
462
  saturated: {
249
- // Every slot full: only a settling worker (the root `xstate.done.actor.*`) moves us.
463
+ // Every slot full: only a settling worker (the root `xstate.done.actor.*` or a caught
464
+ // Failure) moves us.
250
465
  },
251
466
  parked: {
252
467
  // Waiting on the world: wake early on push, or re-query on the poll cadence.
253
468
  on: wake ? { [wake.name]: "discovering" } : {},
254
469
  after: spec.source.pollEvery ? { [spec.source.pollEvery]: { target: "discovering" } } : {},
255
470
  },
471
+ // Not a final state: the run ends `failed`, and the Failure says which items failed.
472
+ ...(report
473
+ ? {}
474
+ : {
475
+ failing: {
476
+ invoke: {
477
+ src: "fail" as const,
478
+ input: ({ context }: { context: unknown }) => {
479
+ const { status, items, failures } = context as PoolCtx;
480
+ const n = Object.keys(failures).length;
481
+ const m = n + Object.keys(items).length;
482
+ return { reason: `${n} of ${m} items failed`, detail: { status, items, failures } };
483
+ },
484
+ },
485
+ },
486
+ }),
256
487
  drained: { type: "final" },
257
488
  deadlocked: { type: "final" },
258
489
  },
@@ -260,6 +491,7 @@ export function pool(worker: AnyStateMachine, spec: PoolSpec<any, any>): AnyStat
260
491
  output: ({ context }): PoolOutput => ({
261
492
  status: (context as unknown as PoolCtx).status ?? "drained",
262
493
  items: (context as unknown as PoolCtx).items,
494
+ failures: (context as unknown as PoolCtx).failures,
263
495
  }),
264
496
  });
265
497