@jr2/orchestrator 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) 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 +442 -0
  10. package/docs/concepts/architecture.md +230 -0
  11. package/docs/concepts/charts.md +143 -0
  12. package/docs/concepts/composition.md +357 -0
  13. package/docs/concepts/converge.md +254 -0
  14. package/docs/concepts/durable-runs.md +189 -0
  15. package/docs/concepts/effects.md +603 -0
  16. package/docs/concepts/gates.md +208 -0
  17. package/docs/concepts/machines-and-workflows.md +289 -0
  18. package/docs/concepts/pools-and-sources.md +230 -0
  19. package/docs/concepts/secrets-and-trust.md +222 -0
  20. package/docs/concepts/the-menu.md +378 -0
  21. package/docs/concepts/workspaces.md +678 -0
  22. package/docs/get-started/a-model-decides.md +563 -0
  23. package/docs/get-started/an-agent-in-a-sandbox.md +381 -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 +922 -0
  27. package/docs/get-started/install.md +126 -0
  28. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  29. package/docs/guides/answer-a-gate.md +304 -0
  30. package/docs/guides/bind-open-parts.md +223 -0
  31. package/docs/guides/cancel-a-run.md +146 -0
  32. package/docs/guides/clean-up.md +206 -0
  33. package/docs/guides/fix-a-failed-converge.md +361 -0
  34. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  35. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  36. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  37. package/docs/guides/look-inside-a-sandbox.md +174 -0
  38. package/docs/guides/mirror-the-kit-images.md +176 -0
  39. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  40. package/docs/guides/open-the-console.md +91 -0
  41. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  42. package/docs/guides/push-and-sign-a-branch.md +486 -0
  43. package/docs/guides/run-a-chart-by-name.md +270 -0
  44. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  45. package/docs/guides/run-one-worker-per-item.md +321 -0
  46. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  47. package/docs/guides/size-a-workspace.md +204 -0
  48. package/docs/guides/start-a-run-from-a-run.md +237 -0
  49. package/docs/guides/start-a-run.md +207 -0
  50. package/docs/guides/upgrade-the-kit-version.md +240 -0
  51. package/docs/guides/use-a-hosted-provider.md +208 -0
  52. package/docs/guides/use-a-local-model.md +313 -0
  53. package/docs/guides/watch-a-run.md +326 -0
  54. package/docs/guides/write-a-workflow.md +318 -0
  55. package/docs/index.md +44 -0
  56. package/docs/llms.txt +216 -0
  57. package/docs/reference/authoring-surface/agent.md +128 -0
  58. package/docs/reference/authoring-surface/answer.md +140 -0
  59. package/docs/reference/authoring-surface/arg.md +60 -0
  60. package/docs/reference/authoring-surface/basic.md +62 -0
  61. package/docs/reference/authoring-surface/bearer.md +53 -0
  62. package/docs/reference/authoring-surface/chartschema.md +23 -0
  63. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  64. package/docs/reference/authoring-surface/command.md +191 -0
  65. package/docs/reference/authoring-surface/customize.md +64 -0
  66. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  67. package/docs/reference/authoring-surface/defineevent.md +114 -0
  68. package/docs/reference/authoring-surface/dispatch.md +126 -0
  69. package/docs/reference/authoring-surface/fail.md +83 -0
  70. package/docs/reference/authoring-surface/file.md +122 -0
  71. package/docs/reference/authoring-surface/frompick.md +277 -0
  72. package/docs/reference/authoring-surface/http.md +159 -0
  73. package/docs/reference/authoring-surface/isfailure.md +58 -0
  74. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  75. package/docs/reference/authoring-surface/open.md +74 -0
  76. package/docs/reference/authoring-surface/openbody.md +86 -0
  77. package/docs/reference/authoring-surface/paramsof.md +125 -0
  78. package/docs/reference/authoring-surface/pool.md +186 -0
  79. package/docs/reference/authoring-surface/push.md +112 -0
  80. package/docs/reference/authoring-surface/secret.md +118 -0
  81. package/docs/reference/authoring-surface/sentby.md +83 -0
  82. package/docs/reference/authoring-surface/source.md +51 -0
  83. package/docs/reference/authoring-surface/text.md +62 -0
  84. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  85. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  86. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  87. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  88. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  89. package/docs/reference/authoring-surface/type-arg.md +19 -0
  90. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  91. package/docs/reference/authoring-surface/type-binding.md +62 -0
  92. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  93. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  94. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  95. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  96. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  97. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  98. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  99. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  100. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  101. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  102. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  103. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  104. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  105. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  106. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  107. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  108. package/docs/reference/authoring-surface/type-childat.md +12 -0
  109. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  110. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  111. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  112. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  113. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  114. package/docs/reference/authoring-surface/type-customize.md +109 -0
  115. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  116. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  117. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  118. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  119. package/docs/reference/authoring-surface/type-duration.md +15 -0
  120. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  121. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  122. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  124. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  125. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  126. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  127. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  128. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  129. package/docs/reference/authoring-surface/type-failure.md +19 -0
  130. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  131. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  132. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  133. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  134. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  135. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  136. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  137. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  138. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  139. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  140. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  141. package/docs/reference/authoring-surface/type-lends.md +38 -0
  142. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  143. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  144. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  145. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  146. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  147. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  148. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  149. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  150. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  151. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  152. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  153. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  154. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  155. package/docs/reference/authoring-surface/type-openof.md +12 -0
  156. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  157. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  158. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  159. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  160. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  161. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  162. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  163. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  164. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  165. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  166. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  167. package/docs/reference/authoring-surface/type-reached.md +12 -0
  168. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  169. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  170. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  171. package/docs/reference/authoring-surface/type-secret.md +25 -0
  172. package/docs/reference/authoring-surface/type-sender.md +19 -0
  173. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  174. package/docs/reference/authoring-surface/type-size.md +85 -0
  175. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  176. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  177. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  178. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  179. package/docs/reference/authoring-surface/type-text.md +19 -0
  180. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  181. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  182. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  183. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  184. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  185. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  186. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  187. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  188. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  189. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  190. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  191. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  192. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  193. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  194. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  195. package/docs/reference/authoring-surface/workspace.md +259 -0
  196. package/docs/reference/authoring-surface.md +197 -0
  197. package/docs/reference/cli.md +433 -0
  198. package/docs/reference/config.md +756 -0
  199. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  200. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  201. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  202. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  203. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  204. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  205. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  206. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  207. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  208. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  209. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  210. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  211. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  212. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  213. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  214. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  215. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  216. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  217. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  218. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  219. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  220. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  221. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  222. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  223. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  224. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  225. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  226. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  227. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  228. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  229. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  230. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  231. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  232. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  233. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  234. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  235. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  236. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  237. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  238. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  239. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  240. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  241. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  242. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  243. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  244. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  245. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  246. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  247. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  248. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  249. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  250. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  251. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  252. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  253. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  254. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  255. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  256. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  257. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  258. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  259. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  260. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  261. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  262. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
  263. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  264. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  265. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  266. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  267. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  268. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  269. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  270. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  271. package/docs/reference/decisions.md +83 -0
  272. package/docs/reference/glossary.md +648 -0
  273. package/docs/reference/http-api.md +1348 -0
  274. package/docs/reference/machines.md +190 -0
  275. package/docs/reference/mechanism-events.md +72 -0
  276. package/docs/why.md +206 -0
  277. package/package.json +5 -3
  278. package/src/actor.ts +541 -79
  279. package/src/agent.ts +114 -38
  280. package/src/ambient.ts +2 -1
  281. package/src/assembly-checks.ts +189 -7
  282. package/src/cause.ts +234 -0
  283. package/src/chart.ts +169 -37
  284. package/src/charts.ts +34 -6
  285. package/src/config.ts +430 -178
  286. package/src/custodian.ts +74 -22
  287. package/src/customize.ts +188 -46
  288. package/src/dispatch-effect.ts +945 -0
  289. package/src/door-compat.ts +217 -0
  290. package/src/durability.ts +11 -4
  291. package/src/effect.ts +341 -44
  292. package/src/effector-wire.ts +160 -11
  293. package/src/fail.ts +209 -0
  294. package/src/harness-client.ts +541 -47
  295. package/src/http-effect.ts +495 -53
  296. package/src/http.ts +427 -16
  297. package/src/index.ts +60 -82
  298. package/src/instance.ts +68 -8
  299. package/src/internal.ts +84 -0
  300. package/src/machine-doc.ts +21 -3
  301. package/src/mechanism-routes.ts +168 -0
  302. package/src/model-keys.generated.ts +53 -0
  303. package/src/model-keys.ts +18 -58
  304. package/src/notices.ts +66 -13
  305. package/src/open-body.ts +159 -23
  306. package/src/open-effect.ts +81 -8
  307. package/src/open.ts +59 -6
  308. package/src/parts.ts +184 -13
  309. package/src/party.ts +68 -2
  310. package/src/pick.ts +463 -11
  311. package/src/pool.ts +247 -36
  312. package/src/registration.ts +83 -4
  313. package/src/repos.ts +2 -1
  314. package/src/run-host.ts +565 -31
  315. package/src/run-output.ts +38 -0
  316. package/src/sandbox-effect.ts +273 -38
  317. package/src/sandbox-kube.ts +3 -1
  318. package/src/secret-placement.ts +52 -17
  319. package/src/server.ts +3 -0
  320. package/src/setup.ts +239 -56
  321. package/src/size.ts +121 -9
  322. package/src/snapshot-store.ts +54 -10
  323. package/src/stub-harness.ts +44 -16
  324. package/src/timers.ts +14 -1
  325. package/src/vocabulary.ts +69 -14
  326. package/src/wire.ts +90 -16
  327. package/src/workspace.ts +697 -175
@@ -0,0 +1,603 @@
1
+ ---
2
+ title: Effects
3
+ description: How a Machine runs a command, calls a URL, pushes a branch or starts a run, at most once.
4
+ ---
5
+
6
+ # Effects
7
+
8
+ An [Effect](../reference/glossary.md#effect) is work that a [Machine](../reference/glossary.md#machine) does outside
9
+ itself. jr2 has four kinds: `command` runs a program in the [Workspace](../reference/glossary.md#workspace), `http`
10
+ calls a URL, `push` pushes the Workspace's branch, and `dispatch` starts a run of a
11
+ [Workflow](../reference/glossary.md#workflow). You write an Effect as an actor slot, beside the
12
+ [Agents](../reference/glossary.md#agent) of the Machine.
13
+
14
+ ## Why Effects exist
15
+
16
+ A Machine often needs a fact from the world: do the tests pass, did the call succeed, is the branch on the remote.
17
+ Without an Effect, the Machine can only ask an Agent to do the work. Then the Machine must trust what the Agent says. An
18
+ Effect gives the Machine its own result, and the Machine routes on it.
19
+
20
+ An Effect also keeps a credential away from the Agent. A `push` uses a push credential, and an `http` call uses an
21
+ [Effect secret](../reference/glossary.md#effect-secret). The Agent never reads either one. jr2 also records each Effect
22
+ before it starts, so that a restart of the [Orchestrator](../reference/glossary.md#orchestrator) does not do the work
23
+ two times. A plain [xstate](https://xstate.js.org) promise has no such record.
24
+
25
+ ## An Effect is an actor slot
26
+
27
+ Put an Effect under `actors` in [`jr2Setup`](../reference/authoring-surface/jr2setup.md). Invoke it by its key from a
28
+ state. The state's `onDone` gets the output of the Effect. This example is a Workflow with a Workspace. An Agent fixes
29
+ the code, and it can ask for a test run with the pick `run_tests`. When the Agent picks `finished`, the Machine pushes
30
+ the branch:
31
+
32
+ **Anthropic**
33
+
34
+ ```ts
35
+ import { z } from "zod";
36
+ import {
37
+ agent,
38
+ answer,
39
+ command,
40
+ defineEvent,
41
+ fail,
42
+ jr2Setup,
43
+ push,
44
+ workspace,
45
+ type CommandOutput,
46
+ type Workspaced,
47
+ } from "@jr2/orchestrator";
48
+
49
+ // The two picks of the Agent.
50
+ const runTests = defineEvent({
51
+ name: "run_tests",
52
+ description: "Run the tests. The result is the answer to this call.",
53
+ input: z.object({}),
54
+ });
55
+ const finished = defineEvent({
56
+ name: "finished",
57
+ description: "Call this when the tests pass and your fix is committed.",
58
+ input: z.object({}),
59
+ });
60
+
61
+ const runInput = z.object({ prompt: z.string(), branch: z.string() });
62
+ type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
63
+
64
+ const passed = (output: CommandOutput): boolean => output.outcome === "exited" && output.code === 0;
65
+
66
+ const body = jr2Setup({
67
+ types: {} as { context: BodyInput; input: BodyInput },
68
+ events: [runTests, finished],
69
+ actors: {
70
+ coder: agent({
71
+ model: "anthropic/claude-haiku-5-5",
72
+ instructions: "You are a careful engineer. You commit your work on the branch.",
73
+ }),
74
+ // Two Effects. Each is an actor slot, beside the Agent.
75
+ tests: command(["node", "--test"], { timeout: "5m" }),
76
+ publish: push(),
77
+ fail: fail(),
78
+ },
79
+ }).createMachine({
80
+ id: "test-and-push",
81
+ context: ({ input }) => input,
82
+ on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
83
+ initial: "coding",
84
+ states: {
85
+ coding: {
86
+ invoke: {
87
+ src: "coder",
88
+ input: ({ context }) => ({ prompt: context.prompt, cwd: context.workspace.repos.app }),
89
+ },
90
+ on: { finished: "publishing" },
91
+ initial: "ready",
92
+ states: {
93
+ ready: { on: { run_tests: "testing" } },
94
+ testing: {
95
+ invoke: {
96
+ src: "tests",
97
+ onDone: {
98
+ target: "ready",
99
+ // The Agent reads this text as the answer to its run_tests pick.
100
+ actions: answer(({ event }) =>
101
+ passed(event.output)
102
+ ? "The tests pass."
103
+ : `The tests fail:\n${"tail" in event.output ? event.output.tail : event.output.reason}`,
104
+ ),
105
+ },
106
+ },
107
+ },
108
+ },
109
+ },
110
+ publishing: {
111
+ invoke: {
112
+ src: "publish",
113
+ onDone: [{ guard: ({ event }) => event.output.outcome === "pushed", target: "done" }, { target: "notPushed" }],
114
+ },
115
+ },
116
+ done: { type: "final" },
117
+ // A Failure ends the body, and jr2 deletes the Sandbox with its commits. To keep them, wait at
118
+ // a Gate before this state.
119
+ notPushed: { invoke: { src: "fail", input: { reason: "the push did not publish the fix" } } },
120
+ failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
121
+ lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
122
+ },
123
+ });
124
+
125
+ export const machine = workspace(body, {
126
+ input: runInput,
127
+ repos: { app: "https://github.com/you/app.git" },
128
+ spec: ({ input }) => ({ branch: input.branch }),
129
+ });
130
+ ```
131
+
132
+ **Local**
133
+
134
+ ```ts
135
+ import { z } from "zod";
136
+ import {
137
+ agent,
138
+ answer,
139
+ command,
140
+ defineEvent,
141
+ fail,
142
+ jr2Setup,
143
+ push,
144
+ workspace,
145
+ type CommandOutput,
146
+ type Workspaced,
147
+ } from "@jr2/orchestrator";
148
+
149
+ // The two picks of the Agent.
150
+ const runTests = defineEvent({
151
+ name: "run_tests",
152
+ description: "Run the tests. The result is the answer to this call.",
153
+ input: z.object({}),
154
+ });
155
+ const finished = defineEvent({
156
+ name: "finished",
157
+ description: "Call this when the tests pass and your fix is committed.",
158
+ input: z.object({}),
159
+ });
160
+
161
+ const runInput = z.object({ prompt: z.string(), branch: z.string() });
162
+ type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
163
+
164
+ const passed = (output: CommandOutput): boolean => output.outcome === "exited" && output.code === 0;
165
+
166
+ const body = jr2Setup({
167
+ types: {} as { context: BodyInput; input: BodyInput },
168
+ events: [runTests, finished],
169
+ actors: {
170
+ coder: agent({
171
+ model: "local/qwen3.6-35b-a3b",
172
+ instructions: "You are a careful engineer. You commit your work on the branch.",
173
+ }),
174
+ // Two Effects. Each is an actor slot, beside the Agent.
175
+ tests: command(["node", "--test"], { timeout: "5m" }),
176
+ publish: push(),
177
+ fail: fail(),
178
+ },
179
+ }).createMachine({
180
+ id: "test-and-push",
181
+ context: ({ input }) => input,
182
+ on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
183
+ initial: "coding",
184
+ states: {
185
+ coding: {
186
+ invoke: {
187
+ src: "coder",
188
+ input: ({ context }) => ({ prompt: context.prompt, cwd: context.workspace.repos.app }),
189
+ },
190
+ on: { finished: "publishing" },
191
+ initial: "ready",
192
+ states: {
193
+ ready: { on: { run_tests: "testing" } },
194
+ testing: {
195
+ invoke: {
196
+ src: "tests",
197
+ onDone: {
198
+ target: "ready",
199
+ // The Agent reads this text as the answer to its run_tests pick.
200
+ actions: answer(({ event }) =>
201
+ passed(event.output)
202
+ ? "The tests pass."
203
+ : `The tests fail:\n${"tail" in event.output ? event.output.tail : event.output.reason}`,
204
+ ),
205
+ },
206
+ },
207
+ },
208
+ },
209
+ },
210
+ publishing: {
211
+ invoke: {
212
+ src: "publish",
213
+ onDone: [{ guard: ({ event }) => event.output.outcome === "pushed", target: "done" }, { target: "notPushed" }],
214
+ },
215
+ },
216
+ done: { type: "final" },
217
+ // A Failure ends the body, and jr2 deletes the Sandbox with its commits. To keep them, wait at
218
+ // a Gate before this state.
219
+ notPushed: { invoke: { src: "fail", input: { reason: "the push did not publish the fix" } } },
220
+ failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
221
+ lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
222
+ },
223
+ });
224
+
225
+ export const machine = workspace(body, {
226
+ input: runInput,
227
+ repos: { app: "https://github.com/you/app.git" },
228
+ spec: ({ input }) => ({ branch: input.branch }),
229
+ });
230
+ ```
231
+
232
+ [Agents and Turns](agents-and-turns.md) tells how to define an Agent.
233
+
234
+ The Effect has the life of the state that invokes it. It starts when the run enters the state, and it stops when the run
235
+ leaves the state. Its input goes into the run's snapshot. A typo in `src` is a compile error.
236
+ [`customize()`](../reference/authoring-surface/customize.md) binds an [Open](../reference/glossary.md#open) part of the
237
+ Effect by its slot key (see [Composition](composition.md)).
238
+
239
+ [`jr2 up`](../reference/cli.md#up) reads each Effect slot without running it, and prints what it found. For each
240
+ Workflow, it lists each Effect with its kind, its argv or its method and host, its timeout, and where it runs. For the
241
+ example above, `jr2 up` prints these lines:
242
+
243
+ ```text
244
+ effects: workflow "test-and-push" — what it runs outside itself (ADR-0067)
245
+ tests: command ["node","--test"] — 300s, in the Harness
246
+ publish: push — 300s, in the Sandbox Effector
247
+ ```
248
+
249
+ The first line gives the name of the Workflow. Each next line gives the slot of one Effect.
250
+
251
+ `jr2 up` also lists each state that invokes [`fail`](../reference/authoring-surface/fail.md), because each one ends the
252
+ run `failed`:
253
+
254
+ ```text
255
+ failures: workflow "test-and-push" — the states that fail the run (ADR-0073)
256
+ test-and-push.notPushed: invokes fail
257
+ test-and-push.failed: invokes fail
258
+ test-and-push.lost: invokes fail
259
+ ```
260
+
261
+ ## The four kinds
262
+
263
+ ### `command`
264
+
265
+ [`command`](../reference/authoring-surface/command.md) runs one program. Its argv is a list, and each entry is one
266
+ argument. jr2 starts `argv[0]` with no shell. An [`arg("name")`](../reference/authoring-surface/arg.md) entry is one
267
+ argument that the input of the Effect fills. Thus an Agent's text cannot become shell syntax. To use a shell, write it
268
+ in the argv: `["sh", "-c", "…"]`. Then the result is the result of your script. The Agent can change the files that the
269
+ script runs, so do not trust its result more than the Agent.
270
+
271
+ A `command` uses the [Worktree](../reference/glossary.md#worktree) of one
272
+ [Repo Slot](../reference/glossary.md#repo-slot): the only slot, or the slot that its `slot` option names. Its default
273
+ timeout is 10 minutes. Its output keeps the end of what the program wrote: 4096 characters by default (the `tail`
274
+ option). The full output goes to the run's feed.
275
+
276
+ ### `http`
277
+
278
+ [`http`](../reference/authoring-surface/http.md) calls a URL. The definition fixes the method, the URL, the headers and
279
+ the auth. The scheme and the host of the URL are literal text. An Effect secret can be in the path of the URL, in a
280
+ header or in the auth, but never in the host. A `request` function makes the part that changes for each run: path
281
+ segments, and a JSON or text body. Thus a value from the run can change a path segment or the body, but not where the
282
+ call goes.
283
+
284
+ In the second example, an Agent with `workspace: "none"` writes one comment, and the Machine posts it on a ticket. This
285
+ Agent has no [Sandbox](../reference/glossary.md#sandbox). The `jira` Effect secret must be in the
286
+ [`secrets`](../reference/config.md#secrets) list of `jr2.config.ts`:
287
+
288
+ **Anthropic**
289
+
290
+ ```ts
291
+ import { z } from "zod";
292
+ import { agent, bearer, defineEvent, fail, fromPick, http, jr2Setup, secret } from "@jr2/orchestrator";
293
+
294
+ const postComment = defineEvent({
295
+ name: "post_comment",
296
+ description: "Post one comment on the ticket.",
297
+ input: z.object({ body: z.string() }),
298
+ });
299
+
300
+ // The host is fixed here. No input can change it.
301
+ const comment = http({
302
+ method: "POST",
303
+ url: "https://jira.example.com/rest/api/2/issue",
304
+ auth: bearer(secret("jira")),
305
+ input: z.object({ ticket: z.string(), body: z.string() }),
306
+ request: ({ ticket, body }) => ({ path: [ticket, "comment"], json: { body } }),
307
+ });
308
+
309
+ export const machine = jr2Setup({
310
+ types: {} as { context: { ticket: string }; input: { ticket: string } },
311
+ events: [postComment],
312
+ actors: {
313
+ triager: agent({
314
+ model: "anthropic/claude-haiku-5-5",
315
+ instructions: "You triage tickets.",
316
+ workspace: "none",
317
+ }),
318
+ comment,
319
+ fail: fail(),
320
+ },
321
+ }).createMachine({
322
+ id: "comment",
323
+ input: z.object({ ticket: z.string() }),
324
+ context: ({ input }) => input,
325
+ on: { "agent.fault": ".failed" },
326
+ initial: "triaging",
327
+ states: {
328
+ triaging: {
329
+ invoke: {
330
+ src: "triager",
331
+ input: ({ context }) => ({ prompt: `Write one comment for ticket ${context.ticket}.` }),
332
+ },
333
+ initial: "ready",
334
+ states: {
335
+ ready: { on: { post_comment: "posting" } },
336
+ posting: {
337
+ invoke: {
338
+ src: "comment",
339
+ // The Agent gives `body`. The Machine gives `ticket`.
340
+ input: fromPick(postComment, ({ context }) => ({ ticket: context.ticket })),
341
+ onDone: "#done",
342
+ },
343
+ },
344
+ },
345
+ },
346
+ done: { id: "done", type: "final" },
347
+ failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
348
+ },
349
+ });
350
+ ```
351
+
352
+ **Local**
353
+
354
+ ```ts
355
+ import { z } from "zod";
356
+ import { agent, bearer, defineEvent, fail, fromPick, http, jr2Setup, secret } from "@jr2/orchestrator";
357
+
358
+ const postComment = defineEvent({
359
+ name: "post_comment",
360
+ description: "Post one comment on the ticket.",
361
+ input: z.object({ body: z.string() }),
362
+ });
363
+
364
+ // The host is fixed here. No input can change it.
365
+ const comment = http({
366
+ method: "POST",
367
+ url: "https://jira.example.com/rest/api/2/issue",
368
+ auth: bearer(secret("jira")),
369
+ input: z.object({ ticket: z.string(), body: z.string() }),
370
+ request: ({ ticket, body }) => ({ path: [ticket, "comment"], json: { body } }),
371
+ });
372
+
373
+ export const machine = jr2Setup({
374
+ types: {} as { context: { ticket: string }; input: { ticket: string } },
375
+ events: [postComment],
376
+ actors: {
377
+ triager: agent({
378
+ model: "local/qwen3.6-35b-a3b",
379
+ instructions: "You triage tickets.",
380
+ workspace: "none",
381
+ }),
382
+ comment,
383
+ fail: fail(),
384
+ },
385
+ }).createMachine({
386
+ id: "comment",
387
+ input: z.object({ ticket: z.string() }),
388
+ context: ({ input }) => input,
389
+ on: { "agent.fault": ".failed" },
390
+ initial: "triaging",
391
+ states: {
392
+ triaging: {
393
+ invoke: {
394
+ src: "triager",
395
+ input: ({ context }) => ({ prompt: `Write one comment for ticket ${context.ticket}.` }),
396
+ },
397
+ initial: "ready",
398
+ states: {
399
+ ready: { on: { post_comment: "posting" } },
400
+ posting: {
401
+ invoke: {
402
+ src: "comment",
403
+ // The Agent gives `body`. The Machine gives `ticket`.
404
+ input: fromPick(postComment, ({ context }) => ({ ticket: context.ticket })),
405
+ onDone: "#done",
406
+ },
407
+ },
408
+ },
409
+ },
410
+ done: { id: "done", type: "final" },
411
+ failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
412
+ },
413
+ });
414
+ ```
415
+
416
+ The body can also be a file of the Worktree: `body: file(path)` (see [`file`](../reference/authoring-surface/file.md)).
417
+ The default timeout of an `http` Effect is 30 seconds.
418
+
419
+ ### `push`
420
+
421
+ [`push`](../reference/authoring-surface/push.md) pushes the Workspace's branch to the
422
+ [Binding](../reference/glossary.md#binding) of its Repo Slot. It uses the push credential of the `git.credentials` entry
423
+ that the URL matches (see [`git.credentials`](../reference/config.md#git-credentials)). The Agent never reads that
424
+ credential. These options change what a push publishes:
425
+
426
+ - `pin`: push the commit that the input names, such as a commit that a [Gate](../reference/glossary.md#gate) approved.
427
+ Without `pin`, jr2 pushes the tip of the Agent's branch when the Effect starts.
428
+ - `sign`: push signed copies of the commits.
429
+ - `squash`: push one commit that holds all the changes, with the message that the input gives.
430
+ - `force`: replace what the remote branch holds.
431
+
432
+ With `pin`, the input gives `{ sha }`. With `squash`, it gives `{ message }`.
433
+
434
+ After a signed or a squashed push, the commits on the remote are not the Agent's own commits. Then jr2 moves the Agent's
435
+ branch to the published commits. The Agent's files, index and stash do not change. The default timeout is 5 minutes.
436
+
437
+ ### `dispatch`
438
+
439
+ [`dispatch`](../reference/authoring-surface/dispatch.md) starts a run of a Workflow. The new run is a run of its own: it
440
+ has its own line in [`jr2 runs`](../reference/cli.md#runs), its own Workspaces and its own `CANCEL`. Its record names
441
+ the run that started it: it is **dispatched by** that run. Use `dispatch` when a Machine waits for work, for example at
442
+ a [Gate](../reference/glossary.md#gate) that a webhook answers, and each item of work needs a run of a Workflow that you
443
+ have. To use a Machine as a part of one run, invoke it in the run instead (see [Composition](composition.md)).
444
+
445
+ - The name of the Workflow is a constant of the definition, as `jr2 run` writes it: `"triage"`, or `"<wrapper>/<name>"`
446
+ for a named [Chart](../reference/glossary.md#chart). An Agent cannot choose it.
447
+ - The `input` of the invoke is the run input of the new run: the same JSON that `jr2 run --input` sends. The `input`
448
+ option of the definition is a zod schema of that run input. It types the invoke, and `jr2 up` compares it with the
449
+ input that the Workflow declares.
450
+ - By default, the Effect ends when the Orchestrator accepts the new run. With `wait: true`, the Effect ends when the new
451
+ run ends, and its output holds the status and the output of that run. The default timeout is 30 seconds, and 10
452
+ minutes with `wait`.
453
+ - With no `target`, the new run starts in the same Orchestrator. With
454
+ `target: { url: "https://<host>", token: secret("<name>") }`, it starts on a different Orchestrator. `url` is the
455
+ origin of that Orchestrator, and the secret holds its Instance token.
456
+ - The new run starts under the current [Assembly](../reference/glossary.md#assembly), as a run of `jr2 run` does.
457
+
458
+ The two runs are independent. A `CANCEL` of one run does not cancel the other. When the run that dispatched ends, the
459
+ new run continues.
460
+
461
+ ## Where an Effect runs
462
+
463
+ You do not choose where an Effect runs. The kind, and what the Effect names, decide it. Each
464
+ [Effector](../reference/glossary.md#effector) is a container that runs jr2's own process, and the Agent cannot run
465
+ commands in it:
466
+
467
+ | The Effect | Where it runs |
468
+ | --------------------------------------- | ----------------------------------------------------------------------------------- |
469
+ | A `command` that names no Effect secret | The [Harness](../reference/glossary.md#harness) container of the Sandbox |
470
+ | A `command` that names an Effect secret | The [Sandbox Effector](../reference/glossary.md#sandbox-effector), against a commit |
471
+ | An `http` Effect | The [Instance Effector](../reference/glossary.md#instance-effector) |
472
+ | An `http` Effect with a Worktree file | The Sandbox Effector |
473
+ | A `push` | The Sandbox Effector |
474
+ | A `dispatch` with no `target` | The Orchestrator, in its own process |
475
+ | A `dispatch` with a `target` | The Instance Effector |
476
+
477
+ A `command` that names no Effect secret runs beside the Agent's [Working tools](../reference/glossary.md#working-tools).
478
+ It runs as the Agent's user, with the tools of the [Sandbox Image](../reference/glossary.md#sandbox-image). It has the
479
+ same [Stand-ins](../reference/glossary.md#stand-in) as the Agent's shell, and no credential of its own.
480
+
481
+ A `command` that names an Effect secret in its `env` runs in the Sandbox Effector, a container in which the Agent runs
482
+ nothing. It does not run in the live Worktree. It runs against one commit: the tip of the branch when it starts, or with
483
+ `pin`, the commit that its input names. When the Worktree held changes that the command did not see, its output has
484
+ `dirty: true`.
485
+
486
+ A `command`, a `push` and an `http` Effect with a file body need a Workspace. Invoke them from the body of a
487
+ [`workspace()`](../reference/authoring-surface/workspace.md). [Architecture](architecture.md) tells what each Effector
488
+ is, and [Secrets and trust](secrets-and-trust.md) tells how an Effector holds the Effect secrets.
489
+
490
+ ## Every outcome is output
491
+
492
+ An Effect gives each outcome to `onDone`, a failure too. Route on the `outcome` field:
493
+
494
+ | Kind | The outcomes | The output type |
495
+ | ---------- | --------------------------------------------------------- | ------------------------------------------------------------------------- |
496
+ | `command` | `exited`, `timeout`, `killed`, `unknown` | [`CommandOutput`](../reference/authoring-surface/type-commandoutput.md) |
497
+ | `http` | `response`, `unreachable`, `refused`, `unknown` | [`HttpOutput`](../reference/authoring-surface/type-httpoutput.md) |
498
+ | `push` | `pushed`, `rejected`, `diverged`, `unknown` | [`PushOutput`](../reference/authoring-surface/type-pushoutput.md) |
499
+ | `dispatch` | `started`, `settled`, `refused`, `unreachable`, `unknown` | [`DispatchOutput`](../reference/authoring-surface/type-dispatchoutput.md) |
500
+
501
+ A test that fails is `exited` with a code that is not 0. A program that the container does not have is `exited` with
502
+ code 127. An HTTP 4xx or 5xx status is a `response`. A Workflow that refuses the run input of a `dispatch` is `refused`,
503
+ with the status code and the report of the refusal. `unknown` means that jr2 cannot tell what the Effect did.
504
+
505
+ The `onError` route of an Effect is only for a defect in the Machine or in the config. For example, the Effect is not in
506
+ a Workspace, or an `arg()` has no value, or an Open part has no binding. An outcome that means that the work did not
507
+ succeed goes to a state that invokes [`fail`](../reference/authoring-surface/fail.md). Then the run ends `failed`, with
508
+ a reason (see [How a run ends](machines-and-workflows.md#how-a-run-ends)). Do not throw to fail: a throw is a defect,
509
+ and the run ends `error`.
510
+
511
+ ## At most once
512
+
513
+ jr2 writes a record of each Effect before the Effect starts, and saves it with the run's snapshot. When the Effect ends,
514
+ jr2 records its output. After an Orchestrator restart, jr2 reads the record:
515
+
516
+ - An Effect that ended gives its output. jr2 does not run it again.
517
+ - An Effect that did not start, starts.
518
+ - An Effect that started follows the rule of its kind.
519
+
520
+ | Kind | After a restart, when the Effect started |
521
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
522
+ | A `command` that names no Effect secret | jr2 attaches to it again. When the Harness does not know it, the outcome is `unknown`. It never runs again. |
523
+ | A `command` that names an Effect secret | jr2 follows it. When the Sandbox Effector lost it, jr2 runs it again against the same commit. With no commit recorded, the outcome is `unknown`. |
524
+ | A `push` | jr2 follows it. When the Sandbox Effector lost it, jr2 pushes the same commit again, and no other commit. With no commit recorded, the outcome is `unknown`. |
525
+ | An `http` Effect | jr2 follows it. When no Effector holds it, jr2 sends it again only if it is `idempotent`. Else the outcome is `unknown`. |
526
+ | A `dispatch` | jr2 sends the start again, with the same key. The Orchestrator answers with the run that the key started, and it starts no second run. |
527
+
528
+ A `command` that names no Effect secret runs the code of the Worktree, and that code can spend a Stand-in. Thus jr2 does
529
+ not run it two times. A `push`, and a `command` that names an Effect secret, run against a commit that jr2 records. Thus
530
+ jr2 can send them again with that commit.
531
+
532
+ To try again, write it in the Machine: a route from the outcome back into the state. Mark an `http` Effect `idempotent`
533
+ only when a second call is safe: the call has its own idempotency key, or it only reads.
534
+
535
+ ## When the state exits
536
+
537
+ When the run leaves the state, the Effect stops:
538
+
539
+ - jr2 kills the process group of a `command` that names no Effect secret.
540
+ - The Sandbox Effector stops a `command` that names an Effect secret. It sends `SIGTERM`, waits 5 seconds, then sends
541
+ `SIGKILL`.
542
+ - An `http` Effect and a `push` have no stop. jr2 stops waiting for the output, but the Effector can still finish the
543
+ work.
544
+ - A `dispatch` with `wait` stops its wait. The run that it started continues.
545
+
546
+ jr2 records the stop, and the Machine gets no output. There is no "finally" in a Machine: cleanup is a state. Put an
547
+ Effect that must not stop in a state of its own. The only exit of that state is its `onDone`. That state handles the
548
+ events of the states above it.
549
+
550
+ ## From a pick to an Effect
551
+
552
+ An Agent can start an Effect with a pick. The pick moves the run into a state that invokes the Effect. Three names help
553
+ you write that state. Each one is optional.
554
+
555
+ - [`fromPick(def, fill)`](../reference/authoring-surface/frompick.md) is the `input` of the state. The params of the
556
+ pick go into the Effect's input, by name. `fill` gives only the keys that the Machine adds. In `comment.ts`, the Agent
557
+ gives `body`, and the Machine gives `ticket`. Thus the Agent cannot post on a different ticket. TypeScript checks the
558
+ sum against the input type of the Effect.
559
+ - [`paramsOf(event, def)`](../reference/authoring-surface/paramsof.md) reads the same params in a plain `input` mapper.
560
+ Use it when the Machine changes a param before the Effect gets it.
561
+ - [`answer(fn)`](../reference/authoring-surface/answer.md) sets what the Agent reads when jr2 answers its pick. In
562
+ `test-and-push.ts`, the Agent reads one line, or the end of the test output. Without `answer`, it reads the
563
+ `CommandOutput` of the command, as JSON. [The Menu](the-menu.md) tells more about `answer`.
564
+
565
+ While the Effect runs below the Agent's state, the Agent's pick is [outstanding](../reference/glossary.md#outstanding).
566
+ The state is not at [Rest](../reference/glossary.md#rest) until the Effect ends. [The Menu](the-menu.md) tells how a
567
+ pick waits for its answer.
568
+
569
+ ## Rules
570
+
571
+ - An Effect is a key of `actors`. `createMachine` refuses an Effect that you write directly in an `invoke`.
572
+ - A `command`, a `push` and an `http` Effect with a file body run only inside a `workspace()`.
573
+ - Under a Workspace with more than one Repo Slot, a `command`, a `push` and a file body name their slot.
574
+ - The host of an `http` URL is literal. An Effect secret never goes in the host, the query or the fragment of a URL.
575
+ - Each Effect secret that an Effect names is in the `secrets` list of `jr2.config.ts`, with a value of 8 characters or
576
+ more. `jr2 up` refuses a name that is not in the list, and a value that is shorter.
577
+ - A `push` with `sign` needs a `sign` field on its `git.credentials` entry. A `squash` without `sign` needs a `sign` or
578
+ a `committer` field there.
579
+ - Each param of a pick that enters an Effect's state has a reader: the Effect's input, `fill`, a guard or an `assign`.
580
+ `createMachine` refuses a param that nothing reads, and a `fill` key that is also a param.
581
+ - A packaged Machine can leave an `http` URL, a `command` argv, one Effect secret name or the `target` of a `dispatch`
582
+ Open. The composer binds it one time with `customize()`. `jr2 up` refuses an Open part that nobody bound.
583
+ - A `dispatch` with no `target` names a Workflow that the Assembly has. `jr2 up` refuses a name that it does not have,
584
+ and an `input` schema that the input of that Workflow does not take.
585
+ - The `url` of a `dispatch` target is an origin only: a scheme, a host and a port, with no path, no query and no user.
586
+
587
+ The guides give the refusal text of each rule.
588
+
589
+ ## Read next
590
+
591
+ - [Run a command or call a URL with an Effect](../guides/run-a-command-or-call-a-url.md)
592
+ - [Push and sign a branch](../guides/push-and-sign-a-branch.md)
593
+ - [Start a run from a run](../guides/start-a-run-from-a-run.md)
594
+ - [Workspaces](workspaces.md), [The Menu](the-menu.md), [Secrets and trust](secrets-and-trust.md)
595
+ - Reference: [`command`](../reference/authoring-surface/command.md), [`http`](../reference/authoring-surface/http.md),
596
+ [`push`](../reference/authoring-surface/push.md), [`dispatch`](../reference/authoring-surface/dispatch.md),
597
+ [`fromPick`](../reference/authoring-surface/frompick.md), [`answer`](../reference/authoring-surface/answer.md)
598
+ - [ADR-0067](../reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md):
599
+ an Effect is an actor slot, and runs at most once unless marked idempotent
600
+ - [ADR-0068](../reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md): an Effector holds
601
+ an Effect's secrets and signs its wire
602
+ - [ADR-0071](../reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md): a run starts a run through a
603
+ `dispatch` Effect