@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/open.ts CHANGED
@@ -5,13 +5,97 @@
5
5
  // and a `workspace()`'s Repo Slots (parts.ts, which already imports agent.ts). A module with
6
6
  // nothing but the sentinel in it is what lets both import it and neither import the other.
7
7
 
8
+ // `Symbol.for`, so an Instance's own copy of this module and the CLI's walk agree on it — a
9
+ // packaged Machine may be built against one and walked by the other.
8
10
  /**
9
- * Open (GLOSSARY.md, ADR-0054): this part is a composer's to bind, with `customize()` — a Repo
10
- * Slot with no url, an Agent with no model. The shape a packaged Machine ships in, because a
11
- * package cannot know the repository or pay for the model. `jr2 up` refuses an Open part nobody
12
- * bound and names the line that binds it; a run never sees one.
11
+ * Leave a part of a Machine Open: for the Instance that registers the Machine to bind with
12
+ * `customize()`. Write it where a value goes, in a Machine that you publish as a package, for a
13
+ * part that the package cannot know or pay for:
14
+ * - a Repo Slot: `repos: { app: open }`, or the whole map: `repos: open`;
15
+ * - an Agent's model: `agent({ model: open, … })`;
16
+ * - an Effect's url or argv: `http({ url: open, … })`, `command(open)`;
17
+ * - the secret of an Effect's auth, of a header, or of a command's `env`: `secret(open)`.
13
18
  *
14
- * `Symbol.for`, so an Instance's own copy of this module and the CLI's walk agree on it — a
15
- * packaged Machine may be built against one and walked by the other.
19
+ * `jr2 up` refuses a Workflow with an Open part that nobody bound, and prints the `customize()`
20
+ * line that binds it: ``refusing: workflow "<name>" leaves <part> open — bind it where the Machine
21
+ * is registered: export const machine = customize(<import>, …) (<adr>)``. A run never sees an
22
+ * Open part.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { z } from "zod";
27
+ * import {
28
+ * agent,
29
+ * customize,
30
+ * defineEvent,
31
+ * fail,
32
+ * isReady,
33
+ * jr2Setup,
34
+ * open,
35
+ * workspace,
36
+ * worktree,
37
+ * type Workspaced,
38
+ * } from "@jr2/orchestrator";
39
+ *
40
+ * const finish = defineEvent({
41
+ * name: "finish",
42
+ * description: "Call this when `node --test` passes.",
43
+ * input: z.object({}),
44
+ * });
45
+ *
46
+ * type Input = Workspaced<{}, "app">;
47
+ *
48
+ * const fixTree = (context: Input) => `${context.workspace.repos.app}/fix` as const;
49
+ *
50
+ * const body = jr2Setup({
51
+ * types: {} as { context: Input; input: Input },
52
+ * events: [finish],
53
+ * actors: {
54
+ * coder: agent({ model: open, instructions: "You fix the failing test." }),
55
+ * cut: worktree(),
56
+ * fail: fail(),
57
+ * },
58
+ * }).createMachine({
59
+ * id: "fix",
60
+ * context: ({ input }) => input,
61
+ * on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
62
+ * initial: "cutting",
63
+ * states: {
64
+ * cutting: {
65
+ * invoke: {
66
+ * src: "cut",
67
+ * input: ({ context }) => ({ path: fixTree(context), branch: "jr2/fix" }),
68
+ * onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
69
+ * },
70
+ * },
71
+ * coding: {
72
+ * invoke: {
73
+ * src: "coder",
74
+ * input: ({ context }) => ({ prompt: "Make `node --test` pass.", cwd: fixTree(context) }),
75
+ * },
76
+ * on: { finish: "done" },
77
+ * },
78
+ * done: { type: "final" },
79
+ * notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
80
+ * failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
81
+ * lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
82
+ * },
83
+ * });
84
+ *
85
+ * // What a package exports: the repository and the model are Open.
86
+ * export const fix = workspace(body, {
87
+ * repos: { app: open },
88
+ * });
89
+ *
90
+ * // What an Instance's `workflows/fix.ts` exports: the same Machine, its Open parts bound.
91
+ * export const machine = customize(fix, {
92
+ * repos: { app: { url: "https://github.com/you/app.git" } },
93
+ * agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
94
+ * });
95
+ * ```
96
+ *
97
+ * @see docs/guides/bind-open-parts.md
98
+ * @see docs/concepts/composition.md
99
+ * @see docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md
16
100
  */
17
101
  export const open: unique symbol = Symbol.for("jr2.open");
package/src/parts.ts CHANGED
@@ -51,10 +51,15 @@ import type { AnyActorRef, AnyStateMachine, StateNode, UnknownActorLogic } from
51
51
  import { isAgent, isOpenAgent, type AgentDeclaration } from "./agent.ts";
52
52
  import { argvText, isEffect, placementOf, type EffectParts, type Placement } from "./effect.ts";
53
53
  import { effectBindingLine, openPartsOf, type OpenEffectPart } from "./open-effect.ts";
54
+ import { isFailSlot } from "./fail.ts";
54
55
  import { isImageContext } from "./images.ts";
55
56
  import { isOpenBody, openBodyDeclarationOf, type OpenBodyDeclaration } from "./open-body.ts";
56
57
  import { open } from "./open.ts";
57
58
  import { repoIdentity } from "./repo-identity.ts";
59
+ import { refuseUnroutedOutcome } from "./effect-checks.ts";
60
+ import { refuseUnroutedAgentFault } from "./mechanism-routes.ts";
61
+ import { refuseSlotActiveTwice } from "./party.ts";
62
+ import { refuseLostOutput } from "./run-output.ts";
58
63
  import { userSeatOf, type Size, type UserContainer } from "./size.ts";
59
64
 
60
65
  // --- Repo Slots (ADR-0051) ---------------------------------------------------------------------
@@ -71,15 +76,102 @@ import { userSeatOf, type Size, type UserContainer } from "./size.ts";
71
76
  // meets it first, and `import { open } from "@jr2/orchestrator"` is the only spelling anyone writes.
72
77
  export { open } from "./open.ts";
73
78
 
74
- /** What a Repo Slot resolves to (GLOSSARY.md "Binding"): the url, and the base the branch
75
- * Worktree is cut from — absent, the Repo's own default branch. */
76
- export type Binding = { url: string; ref?: string };
79
+ /**
80
+ * The repository of a Repo Slot: `{ url, ref }`. `url` is the Repo's git url. `ref` is the branch,
81
+ * the tag or the commit that the clone stays on, and that a new branch starts from; absent, the
82
+ * Repo's default branch. Where only a url is needed, a string is enough.
83
+ *
84
+ * `workspace()` and `customize()` refuse:
85
+ * - an empty `url`: ``<where>: Repo Slot "<slot>" is bound to an empty url (got <value>) — a slot
86
+ * is `open`, a url, `{ url, ref? }`, or a mapper `({ input }) => url | { url, ref? }` over the
87
+ * door (ADR-0051).``
88
+ * - an empty `ref`: the same text, with `bound with an empty ref` in place of `bound to an empty
89
+ * url`.
90
+ * - a url that names no Repo, such as a relative path: ``<where>: Repo Slot "<slot>" binds
91
+ * "<url>", which names no Repo — <reason> (ADR-0051).``
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * import { jr2Setup, workspace, type Binding } from "@jr2/orchestrator";
96
+ *
97
+ * const app: Binding = { url: "https://github.com/you/app.git", ref: "release-2" };
98
+ *
99
+ * const body = jr2Setup().createMachine({
100
+ * id: "body",
101
+ * initial: "done",
102
+ * states: { done: { type: "final" } },
103
+ * });
104
+ *
105
+ * export const machine = workspace(body, {
106
+ * repos: { app },
107
+ * });
108
+ * ```
109
+ *
110
+ * @see docs/concepts/workspaces.md
111
+ * @see docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md
112
+ */
113
+ // What a Repo Slot resolves to (GLOSSARY.md "Binding").
114
+ export type Binding = {
115
+ /** The Repo's git url. */
116
+ url: string;
117
+ /** The branch, tag or commit that the clone stays on, and that a new branch starts from. Default:
118
+ * the Repo's default branch. */
119
+ ref?: string;
120
+ };
77
121
 
122
+ // Bound and open are what `jr2 up` can see; per-run is the run's business, fenced at attach by
123
+ // `git.credentials` (ADR-0051).
78
124
  /**
79
- * One Repo Slot's value: BOUND (a url, or `{ url, ref? }`), OPEN (the {@link open} sentinel —
80
- * someone downstream binds it), or PER-RUN (a mapper over the wrapper's door, so a run input — a
81
- * ticket field — decides). Bound and open are what `jr2 up` can see; per-run is the run's business,
82
- * fenced at attach by `git.credentials` (ADR-0051).
125
+ * The value of one Repo Slot in `workspace({ repos })`. One of:
126
+ * - a url, or a {@link Binding} `{ url, ref }`: the Machine names the Repo.
127
+ * - {@link open}: the Instance that registers the Machine binds it with `customize()`. `jr2 up`
128
+ * refuses a Workflow that leaves it open: ``refusing: workflow "<name>" leaves Repo Slot
129
+ * "<slot>" open — bind it where the Machine is registered: export const machine =
130
+ * customize(<import>, …) (ADR-0051)``, with the whole `customize` line to paste.
131
+ * - a function of the run input, `({ input }) => url` or a {@link Binding}: each run chooses. The
132
+ * url must match an entry of `git.credentials` in `jr2.config.ts`, or the run cannot use it.
133
+ *
134
+ * The key of the slot is a directory name under `/work`. It starts with a letter, so never with
135
+ * `.`: `/work/.jr2/` is reserved for jr2. `workspace()` and `customize()` refuse a key that does
136
+ * not: ``<where>: Repo Slot key "<key>" is not a directory name — a slot becomes `/work/<slot>`,
137
+ * `/work/.jr2/` is reserved for jr2, and its order matters, so it starts with a letter and matches
138
+ * /^[A-Za-z][A-Za-z0-9._-]*$/ (ADR-0051, ADR-0075).`` They refuse a value of another
139
+ * shape: ``<where>: Repo Slot "<slot>" is <what> — a slot is `open`, a url, `{ url, ref? }`, or a
140
+ * mapper `({ input }) => url | { url, ref? }` over the door (ADR-0051).``
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import { z } from "zod";
145
+ * import { jr2Setup, open, workspace, type RepoSlot, type Workspaced } from "@jr2/orchestrator";
146
+ *
147
+ * const runInput = z.object({ service: z.string() });
148
+ * type RunInput = z.infer<typeof runInput>;
149
+ *
150
+ * const service: RepoSlot<RunInput> = ({ input }) => `https://github.com/you/${input.service}.git`;
151
+ *
152
+ * const body = jr2Setup({
153
+ * types: {} as { input: Workspaced<RunInput, "service" | "docs" | "config"> },
154
+ * }).createMachine({
155
+ * id: "body",
156
+ * initial: "done",
157
+ * states: { done: { type: "final" } },
158
+ * });
159
+ *
160
+ * // A Machine for a package: the Instance that registers it binds `config`.
161
+ * export const deploy = workspace(body, {
162
+ * input: runInput,
163
+ * repos: {
164
+ * service,
165
+ * docs: "https://github.com/you/docs.git",
166
+ * config: open,
167
+ * },
168
+ * });
169
+ * ```
170
+ *
171
+ * @see docs/concepts/workspaces.md
172
+ * @see docs/guides/bind-open-parts.md
173
+ * @see docs/guides/give-the-cluster-git-credentials.md
174
+ * @see docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md
83
175
  */
84
176
  export type RepoSlot<TInput = unknown> =
85
177
  | typeof open
@@ -104,12 +196,13 @@ export function repoSlotState(value: RepoSlot<any>): RepoSlotState {
104
196
  * carry — and to what a prompt can name without quoting. It starts with a LETTER: the handles
105
197
  * keep declaration order, which a Machine may give a meaning to (`task`: the first slot is the
106
198
  * one the coder edits), and JS puts an integer-like key such as `"1"` ahead of every other key in
107
- * `Object.keys`, wherever the author wrote it. */
199
+ * `Object.keys`, wherever the author wrote it. So a key never starts with `.`, and `/work/.jr2/`
200
+ * stays jr2's (ADR-0075). */
108
201
  export const REPO_SLOT_KEY = /^[A-Za-z][A-Za-z0-9._-]*$/;
109
202
 
110
203
  /**
111
- * Refuse a malformed slot value BY NAME, at build time — the same derives-from-a-typo class the
112
- * spec guard catches for the branch, one build earlier. A bound url is also parsed here: an
204
+ * Refuse a malformed slot value BY NAME, at build time — a derives-from-a-typo fault, one build
205
+ * earlier. A bound url is also parsed here: an
113
206
  * identity the walk cannot derive (a relative path, an unknown scheme) is refused where the
114
207
  * author wrote it rather than at the converge that walks it. `where` names the caller
115
208
  * (`workspace()`, `customize()`).
@@ -118,8 +211,8 @@ export function assertRepoSlot(where: string, slot: string, value: unknown): ass
118
211
  if (!REPO_SLOT_KEY.test(slot)) {
119
212
  throw new Error(
120
213
  `${where}: Repo Slot key ${JSON.stringify(slot)} is not a directory name — a slot becomes ` +
121
- "`/work/<slot>` and its order matters, so it starts with a letter and matches " +
122
- "/^[A-Za-z][A-Za-z0-9._-]*$/ (ADR-0051).",
214
+ "`/work/<slot>`, `/work/.jr2/` is reserved for jr2, and its order matters, so it starts with a letter " +
215
+ "and matches /^[A-Za-z][A-Za-z0-9._-]*$/ (ADR-0051, ADR-0075).",
123
216
  );
124
217
  }
125
218
  if (value === open || typeof value === "function") return;
@@ -151,9 +244,8 @@ export function assertRepoSlot(where: string, slot: string, value: unknown): ass
151
244
  * to a concrete ref on the port's side, which is what keeps the Machine cluster-agnostic. The
152
245
  * Repos are Slots, each bound, open, or per-run.
153
246
  *
154
- * They are options rather than `WorkspaceSpec` fields because they are STATIC: `jr2 up` must find
155
- * them by walking the Machine, and a per-run spec is a function of run input that no walk can
156
- * evaluate.
247
+ * They are static options because `jr2 up` must find them by walking the Machine, and no walk can
248
+ * evaluate a function of run input.
157
249
  */
158
250
  export type SandboxParts = {
159
251
  /** The Sandbox Image. Absent → the Instance's `images/default`, then the stock Harness. */
@@ -167,6 +259,8 @@ export type SandboxParts = {
167
259
  /** The Size the Machine states (ADR-0060) — the `workspace()`'s, with any `customize()` layered
168
260
  * over it. Absent → the Instance's `sandbox.resources`, then the kit default. */
169
261
  resources?: Size;
262
+ /** The pod's work group (ADR-0005). Absent: the port's default. */
263
+ workGroup?: number;
170
264
  /** The Held secrets this Workspace's pod gets beside its Agents' model keys (ADR-0059): names of
171
265
  * the `secrets` list. Naming is placement — no other pod gets a name for being listed here. */
172
266
  secrets?: string[];
@@ -199,8 +293,8 @@ export function sandboxPartsOf(machine: AnyStateMachine | undefined): SandboxPar
199
293
 
200
294
  /** Does this Machine COMPOSE a Sandbox — i.e. is it a `workspace()` wrapper? Distinct from
201
295
  * `sandboxPartsOf(m)` naming no image: a wrapper that names neither image still owns the two
202
- * seats, which is what `customize({ image })` retunes (ADR-0049) — and it always carries at least
203
- * one Repo Slot (ADR-0051). Also the data-plane switch, read off the walk: an Instance needs
296
+ * seats, which is what `customize({ image })` retunes (ADR-0049) — and it may carry no Repo Slot
297
+ * at all (ADR-0051). Also the data-plane switch, read off the walk: an Instance needs
204
298
  * Sandboxes exactly when a registered Machine composes one. A logic with no `config` (an Effect's,
205
299
  * ADR-0067) composes none. */
206
300
  export function composesSandbox(machine: AnyStateMachine): boolean {
@@ -470,14 +564,15 @@ export type CarriedEffect = {
470
564
  (| {
471
565
  kind: "command";
472
566
  argv: string[];
473
- slot?: string;
474
567
  secrets?: string[];
475
568
  pin?: true;
476
569
  open?: Array<"argv" | "secret">;
570
+ /** A `worktree()`: its `expect`. A report prints it in place of the kit program's argv. */
571
+ cut?: { expect?: "new" | "existing" };
477
572
  }
478
573
  /** An http Effect (ADR-0068): its method, its fixed target, and the secrets it names — what
479
574
  * places each secret, and what its Effector's config allows. `file`: its body is a Worktree
480
- * file (ADR-0067), so it runs in its Workspace's Sandbox Effector, on Repo Slot `slot` when named.
575
+ * file (ADR-0067), so it runs in its Workspace's Sandbox Effector.
481
576
  * An Open url has no target, and names only the secrets its headers and auth name. */
482
577
  | {
483
578
  kind: "http";
@@ -486,17 +581,39 @@ export type CarriedEffect = {
486
581
  secrets: string[];
487
582
  idempotent: boolean;
488
583
  file?: true;
489
- slot?: string;
490
584
  open?: Array<"url" | "secret">;
491
585
  }
492
- /** A push (ADR-0067, ADR-0051): the Workspace's branch to its Repo Slot's Binding. It names no
586
+ /** A push (ADR-0067, ADR-0075): a cut Worktree's branch to its Repo Slot's Binding. It names no
493
587
  * secret: `jr2 up` resolves its credential through `git.credentials`. */
494
588
  | { kind: "push"; slot?: string; sign: boolean; squash: boolean; force: boolean; pin: boolean; secrets: string[] }
589
+ /** A dispatch (ADR-0071): the Workflow it starts a run of, as `jr2 run` names it, whether it
590
+ * waits, and — toward another Orchestrator — the target's origin as an http target and its
591
+ * token as the one secret (placed in the Instance Effector beside the http Effects). No target:
592
+ * it runs in the Orchestrator's own process and names no secret. `input`: the slot's input
593
+ * schema as JSON Schema, what the walk compares with the callee's door; absent, the door alone
594
+ * checks. An Open target has no target and no secret yet. */
595
+ | {
596
+ kind: "dispatch";
597
+ workflow: string;
598
+ wait: boolean;
599
+ target?: { scheme: "http" | "https"; host: string; port: number };
600
+ secrets: string[];
601
+ input?: Record<string, unknown>;
602
+ open?: Array<"target">;
603
+ }
495
604
  );
496
605
 
606
+ /** One state that can fail the run (ADR-0073): it invokes a `fail()` slot. `state` is the state's
607
+ * id, `machine` the id of its Machine, `slot` the slot key it invokes, and `path` the `customize()`
608
+ * route to its Machine, as an {@link OpenSlot}'s. Static, as a Gate or an Effect is: the walk reads
609
+ * it, and `jr2 up`, `jr2 check` and the Machine doc list it. */
610
+ export type CarriedFailure = { machine: string; state: string; slot: string; path: OpenSlot["path"] };
611
+
497
612
  /** Everything the registered Machines carry that a converge or a boot must act on. */
498
613
  export type CarriedParts = {
499
614
  agents: CarriedAgent[];
615
+ /** Every state that invokes a `fail()` slot (ADR-0073), in walk order, nested Machines too. */
616
+ failures: CarriedFailure[];
500
617
  /** Every Effect slot, in walk order — one per Machine and slot key, at the first route the walk
501
618
  * reached its Machine by. The placement reads {@link placedEffectsOf}: one per route. */
502
619
  effects: CarriedEffect[];
@@ -556,17 +673,18 @@ function inlineMachines(node: StateNode<any, any>): Array<{ machine: AnyStateMac
556
673
  }
557
674
 
558
675
  /**
559
- * Refuse a Part written inline on an `invoke.src` — an `agent()`, an Effect or a `workspace()` that
676
+ * Refuse a Part written inline on an `invoke.src` — an `agent()`, an Effect, a `fail()` or a `workspace()` that
560
677
  * is no key of the Machine's `actors` (see the module comment). Thrown at build by
561
678
  * `jr2Setup.createMachine` and at load by {@link partsOf}, so a Machine built with xstate's own
562
- * `createMachine` is refused where `jr2 up` and the Orchestrator read it. A plain child Machine
563
- * written inline is not a Part: it stays walked, with no `customize()` route (its `path` is
564
- * `undefined`).
679
+ * `createMachine` is refused where `jr2 check`, `jr2 up` and the Orchestrator read it. A plain
680
+ * child Machine written inline is not a Part: it stays walked, with no `customize()` route (its
681
+ * `path` is `undefined`).
565
682
  */
566
683
  export function refuseInlineParts(machine: AnyStateMachine): void {
567
684
  const partOf = (src: unknown): { part: string; written: string } | undefined => {
568
685
  if (isAgent(src)) return { part: "an Agent", written: "agent(...)" };
569
686
  if (isEffect(src)) return { part: "an Effect", written: `${src.effect.kind}(...)` };
687
+ if (isFailSlot(src)) return { part: "a fail() Part", written: "fail()" };
570
688
  const child = asMachine(src);
571
689
  if (child && composesSandbox(child)) return { part: "a Workspace", written: "workspace(...)" };
572
690
  return undefined;
@@ -615,6 +733,7 @@ function canonical(value: unknown): string {
615
733
  */
616
734
  export function partsOf(machines: Iterable<AnyStateMachine>): CarriedParts {
617
735
  const agents: CarriedAgent[] = [];
736
+ const failures: CarriedFailure[] = [];
618
737
  const effects: CarriedEffect[] = [];
619
738
  const images: CarriedImage[] = [];
620
739
  const repos: CarriedRepo[] = [];
@@ -690,6 +809,11 @@ export function partsOf(machines: Iterable<AnyStateMachine>): CarriedParts {
690
809
  if (walked.has(machine)) return;
691
810
  walked.add(machine);
692
811
  refuseInlineParts(machine);
812
+ refuseLostOutput(machine);
813
+ refuseUnroutedAgentFault(machine);
814
+ refuseUnroutedOutcome(machine);
815
+ // Here too, for a Machine built with xstate's own setup: the Turns' identity is the runtime's.
816
+ refuseSlotActiveTwice(machine);
693
817
  const parts = sandboxPartsOf(machine);
694
818
  const user = userSeatOf(parts.user);
695
819
  collectImage(parts.image);
@@ -706,6 +830,7 @@ export function partsOf(machines: Iterable<AnyStateMachine>): CarriedParts {
706
830
  });
707
831
  }
708
832
  const body = wrapperBodyOf(machine);
833
+ for (const { state, slot } of failingStatesOf(machine)) failures.push({ machine: machine.id, state, slot, path });
709
834
  for (const [name, logic] of Object.entries(machine.implementations.actors as Record<string, unknown>)) {
710
835
  if (isAgent(logic)) collectAgent(path, name, logic.definition);
711
836
  else if (isOpenBody(logic)) {
@@ -735,6 +860,7 @@ export function partsOf(machines: Iterable<AnyStateMachine>): CarriedParts {
735
860
  for (const body of noneBodies) for (const m of body.declaration.allows.models) harnessModels.add(m);
736
861
  return {
737
862
  agents,
863
+ failures,
738
864
  effects,
739
865
  images,
740
866
  repos,
@@ -817,6 +943,23 @@ export function carriedEffectOf(
817
943
  ): CarriedEffect {
818
944
  const placement = placementOf(parts);
819
945
  const openParts = openPartsOf(parts);
946
+ if (parts.kind === "dispatch") {
947
+ const input = (parts as { [DISPATCH_INPUT]?: Record<string, unknown> })[DISPATCH_INPUT];
948
+ return {
949
+ name,
950
+ path,
951
+ route,
952
+ kind: "dispatch",
953
+ placement,
954
+ workflow: parts.workflow,
955
+ wait: parts.wait,
956
+ timeoutMs: parts.timeoutMs,
957
+ ...(parts.target === undefined || parts.target === open ? {} : { target: originTarget(parts.target.url) }),
958
+ secrets: [...parts.secrets],
959
+ ...(input !== undefined ? { input } : {}),
960
+ ...(openParts.length ? { open: openParts as Array<"target"> } : {}),
961
+ };
962
+ }
820
963
  if (parts.kind === "http") {
821
964
  return {
822
965
  name,
@@ -831,7 +974,6 @@ export function carriedEffectOf(
831
974
  idempotent: parts.idempotent,
832
975
  timeoutMs: parts.timeoutMs,
833
976
  ...(parts.file ? { file: true as const } : {}),
834
- ...(parts.file?.slot !== undefined ? { slot: parts.file.slot } : {}),
835
977
  };
836
978
  }
837
979
  if (parts.kind === "push") {
@@ -851,7 +993,7 @@ export function carriedEffectOf(
851
993
  ...(slot !== undefined ? { slot } : {}),
852
994
  };
853
995
  }
854
- const { argv, timeoutMs, slot, secrets, pin } = parts;
996
+ const { argv, timeoutMs, secrets, pin } = parts;
855
997
  return {
856
998
  name,
857
999
  path,
@@ -860,13 +1002,27 @@ export function carriedEffectOf(
860
1002
  placement,
861
1003
  argv: argvText(argv),
862
1004
  timeoutMs,
863
- ...(slot !== undefined ? { slot } : {}),
864
1005
  ...(secrets?.length ? { secrets: [...secrets] } : {}),
865
1006
  ...(pin ? { pin } : {}),
866
1007
  ...(openParts.length ? { open: openParts as Array<"argv" | "secret"> } : {}),
1008
+ ...(parts.cut ? { cut: { ...parts.cut } } : {}),
867
1009
  };
868
1010
  }
869
1011
 
1012
+ /** The key a dispatch's slot schema rides its parts under (dispatch-effect.ts): `Symbol.for`, so this
1013
+ * walk reads it off a Machine built by another copy of the module (ADR-0071). */
1014
+ const DISPATCH_INPUT = Symbol.for("jr2.dispatch.input");
1015
+
1016
+ /** A dispatch target's origin as the Effector's fence reads an http target: scheme, host, port. The
1017
+ * origin was checked when the slot was built, so it parses. */
1018
+ function originTarget(url: string): { scheme: "http" | "https"; host: string; port: number } {
1019
+ const u = new URL(url);
1020
+ const scheme = u.protocol === "http:" ? "http" : "https";
1021
+ // As an http target's: brackets off an IPv6 literal, lower case (http-effect.ts `targetOf`).
1022
+ const host = u.hostname.replace(/^\[|\]$/g, "").toLowerCase();
1023
+ return { scheme, host, port: u.port ? Number(u.port) : scheme === "http" ? 80 : 443 };
1024
+ }
1025
+
870
1026
  /**
871
1027
  * One Workspace a Machine composes, as its pod's Held secrets are placed (ADR-0059): what the pod
872
1028
  * names. Located like a {@link CarriedSize} — `path` is the `customize()` route to its
@@ -888,6 +1044,9 @@ export type WorkspacePod = {
888
1044
  * credentialed `command` slot below the `workspace()`, down to the next `workspace()` — read off
889
1045
  * their plain data, statically. Empty → the pod gets no Sandbox Effector and no Secret. */
890
1046
  effects: CarriedEffect[];
1047
+ /** The slot of each `worktree()` below the `workspace()`, down to the next `workspace()`. A cut runs
1048
+ * in the Harness container, so `effects` does not hold it. Absent when there is none. */
1049
+ cuts?: string[];
891
1050
  /** The `workspace()`'s `effector: { image }`, as written. Absent → the Sandbox Image. */
892
1051
  effector?: { image: string };
893
1052
  /** The `workspace()`'s own `image`, as written — its Sandbox Image. Absent → `images/default`,
@@ -896,6 +1055,9 @@ export type WorkspacePod = {
896
1055
  /** Each Repo Slot's Binding url as the Machine binds it, by slot, in order; undefined for an open
897
1056
  * or per-run slot, whose url `jr2 up` cannot read. A push resolves its credential through it. */
898
1057
  bindings: Record<string, string | undefined>;
1058
+ /** The `workspace()` declares no Repo Slot (ADR-0051): it has only `/work`, which its steps
1059
+ * share. Absent for one with slots, and for an Open map, whose slots the composer names. */
1060
+ noSlot?: true;
899
1061
  };
900
1062
 
901
1063
  /**
@@ -913,6 +1075,7 @@ export function workspacePodsOf(root: AnyStateMachine): WorkspacePod[] {
913
1075
  models: Set<string>,
914
1076
  on: Set<AnyStateMachine>,
915
1077
  effects: Map<string, CarriedEffect>,
1078
+ cuts: Set<string>,
916
1079
  path: OpenSlot["path"],
917
1080
  route: PlacementRoute,
918
1081
  ): void => {
@@ -920,7 +1083,7 @@ export function workspacePodsOf(root: AnyStateMachine): WorkspacePod[] {
920
1083
  on.add(machine);
921
1084
  const body = wrapperBodyOf(machine);
922
1085
  const visit = (child: AnyStateMachine, at: OpenSlot["path"], to: PlacementRoute) => {
923
- if (!composesSandbox(child)) modelsIn(child, models, on, effects, at, to);
1086
+ if (!composesSandbox(child)) modelsIn(child, models, on, effects, cuts, at, to);
924
1087
  };
925
1088
  for (const [name, logic] of Object.entries(machine.implementations.actors as Record<string, unknown>)) {
926
1089
  if (isAgent(logic)) {
@@ -939,6 +1102,7 @@ export function workspacePodsOf(root: AnyStateMachine): WorkspacePod[] {
939
1102
  if (isEffect(logic)) {
940
1103
  const effect = carriedEffectOf(name, path, route, logic.effect);
941
1104
  if (effect.placement === "sandbox") effects.set(JSON.stringify([route, name]), effect);
1105
+ if (logic.effect.kind === "command" && logic.effect.cut) cuts.add(name);
942
1106
  continue;
943
1107
  }
944
1108
  const child = asMachine(logic);
@@ -958,7 +1122,8 @@ export function workspacePodsOf(root: AnyStateMachine): WorkspacePod[] {
958
1122
  if (composesSandbox(machine)) {
959
1123
  const models = new Set<string>();
960
1124
  const effects = new Map<string, CarriedEffect>();
961
- modelsIn(machine, models, new Set(), effects, path, route);
1125
+ const cuts = new Set<string>();
1126
+ modelsIn(machine, models, new Set(), effects, cuts, path, route);
962
1127
  const parts = sandboxPartsOf(machine);
963
1128
  const bindings: Record<string, string | undefined> = {};
964
1129
  if (parts.repos !== open) {
@@ -973,9 +1138,11 @@ export function workspacePodsOf(root: AnyStateMachine): WorkspacePod[] {
973
1138
  secrets: [...(parts.secrets ?? [])],
974
1139
  models: [...models].sort(),
975
1140
  effects: [...effects.values()],
1141
+ ...(cuts.size ? { cuts: [...cuts] } : {}),
976
1142
  ...(parts.effector ? { effector: { ...parts.effector } } : {}),
977
1143
  ...(parts.image !== undefined ? { image: parts.image } : {}),
978
1144
  bindings,
1145
+ ...(parts.repos !== open && Object.keys(parts.repos).length === 0 ? { noSlot: true as const } : {}),
979
1146
  });
980
1147
  }
981
1148
  const body = wrapperBodyOf(machine);
@@ -990,6 +1157,22 @@ export function workspacePodsOf(root: AnyStateMachine): WorkspacePod[] {
990
1157
  return pods;
991
1158
  }
992
1159
 
1160
+ /** Every state of `machine`'s own tree that invokes a `fail()` slot (ADR-0073), in document order,
1161
+ * with the slot key: a state that can fail the run. A child Machine's states are its own walk's. */
1162
+ export function failingStatesOf(machine: AnyStateMachine): Array<{ state: string; slot: string }> {
1163
+ const actors = machine.implementations.actors as Record<string, unknown>;
1164
+ const out: Array<{ state: string; slot: string }> = [];
1165
+ const visit = (node: StateNode<any, any>): void => {
1166
+ for (const inv of node.invoke) {
1167
+ if (typeof inv.src === "string" && isFailSlot(actors[inv.src])) out.push({ state: node.id, slot: inv.src });
1168
+ }
1169
+ const children = Object.values(node.states as Record<string, StateNode<any, any>>);
1170
+ for (const child of children.sort((a, b) => a.order - b.order)) visit(child);
1171
+ };
1172
+ visit(machine.root);
1173
+ return out;
1174
+ }
1175
+
993
1176
  /** Every machine object written inline below `node`, at any depth of its state tree, with its hop. */
994
1177
  function walkStates(node: StateNode<any, any>, visit: (machine: AnyStateMachine, hop: RouteHop) => void): void {
995
1178
  for (const { machine, hop } of inlineMachines(node)) visit(machine, hop);