@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
@@ -0,0 +1,183 @@
1
+ # A run starts a run through a `dispatch` Effect
2
+
3
+ [ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md) made composition `import` plus invoke, one run
4
+ and one run id, and rejected a run that starts a run "for no requirement that needs a second run id". The grill of
5
+ 2026-10-20 brought the requirement: a long-running Machine that watches the outside (a poll of GitHub issues, a webhook
6
+ into a Gate) and, when something arrives, runs an existing Workflow on it, where that Workflow needs its own run: its
7
+ own row in `jr2 runs`, its own durability and cancel, its own Workspaces, a life that is not the watcher's, and perhaps
8
+ an Orchestrator that is not the watcher's. GitHub's `workflow_dispatch` through the API, an Argo Events sensor that
9
+ submits a Workflow, and Temporal's own advice for a child in another namespace (an Activity that starts a workflow
10
+ through the client) are all the same shape: a separate, independent run, started as an act of the caller. That is not
11
+ composition and does not replace it. The two uses stay two mechanisms.
12
+
13
+ ## Decision
14
+
15
+ - **`dispatch` is a fourth Effect kind**, beside `command`, `http` and `push`
16
+ ([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)): an actor slot,
17
+ `actors: { review: dispatch("review", { input?, wait?, target? }) }`, with a ledger row, at-most-once, stopped when
18
+ its state exits, `fromPick` between a pick and it, and its output the answer to the pick that led to it. An Agent that
19
+ "calls a workflow" is an ordinary pick whose state invokes the slot; nothing a model reads changes, so there is no
20
+ `@model` claim. **The callee is named as `jr2 run` names it**, `review` or `task/review-loop`, a string constant on
21
+ the slot and never an `arg()` a model fills: the name is authority, the input is data. A run needs a Workflow name
22
+ (status, drift, the Console), so a registered Workflow is the only thing a dispatch can start, which is why a name is
23
+ right here where ADR-0049 rejects it for a child: invoke-by-name resolves to a Machine inside the caller's run; a
24
+ dispatch resolves to a run of its own. One slot per callee; a Machine that calls two Workflows declares two slots.
25
+ - **The invoke's input is the run's body, whole.** `input: ({ context }) => ({ ticket })` or
26
+ `input: fromPick(review, ({ context }) => ({ repo: context.repo }))` is the JSON that `POST /workflows/<name>/runs`
27
+ validates ([ADR-0033](0033-a-machine-declares-the-input-that-starts-it.md)), the same body `jr2 run --input` sends. A
28
+ one-shot Chart start is nothing special, `{ chart }` in the body; a named Chart is a name. Every knob is the slot's
29
+ (`wait`, `target`, `input`), so a run-start option that arrives later is a slot option or a door field, never a
30
+ wrapper around the body. Accepted with it: the pick's schema and the callee's door are two schemas the author keeps
31
+ aligned, and the idiom is to derive one from the other (`reviewDoor.pick({ branch: true })`); the handoff is by
32
+ reference, a url, a branch and a commit the callee's own Workspace clones, never a Worktree file, because the Effect
33
+ runs where no Worktree is; no secret rides the body (GitHub's `secrets: inherit` has no counterpart: the callee's
34
+ Assembly bound its own); `wait` is static, so a Machine that needs both forms declares two slots; the body is plain
35
+ JSON; and the callee's host-injected fields (`instanceId`) are the callee's host's, written after the parse. `input`
36
+ on the slot is an optional zod schema: the author's claim about the callee's door, which types the mapper and
37
+ `fromPick` and is what `jr2 up` compares (below). Without it the mapper returns `unknown` and the door alone checks.
38
+ - **Two placements, read from the target, and the author writes none.** With no `target` the Effect runs in the
39
+ Orchestrator's own process and starts the run through the run host and the ledger directly, no wire and no token: the
40
+ Orchestrator holds the Instance token already
41
+ ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)) and is the single writer of runs, so a
42
+ start that went out through an Effector and back in over HTTP would carry a token for nothing. That is a fourth
43
+ Placement, `orchestrator`, and this is the one Effect that has it. With a target,
44
+ `target: { url, token: secret("ops-instance") }`, the Effect runs in the Instance Effector, which gains a `dispatch`
45
+ kind, holds the token as an Effect secret and calls the foreign door; the url is an origin, scheme and host and a
46
+ port, and a path, a query or userinfo in it is refused at build, since the Effector appends the door's path and the
47
+ origin is what its fence (`effector.json`) names. A packaged Machine leaves it `target: open` and the composer binds
48
+ the url and the token together, one Open part, with `customize(m, { effects: { task: { target: { url, token } } } })`
49
+ ([ADR-0054](0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)); `secret(open)` inside a
50
+ target is refused, since the whole target is the part. Another Instance may run another Kit version, so the HTTP door
51
+ is the only contract there. The token is the Instance token, which unlocks all control
52
+ ([ADR-0032](0032-the-console-unlocks-with-the-instance-token.md)); a start-only token is a token kind of its own and
53
+ is deferred.
54
+ - **Two settlements, `started` by default, and every outcome is output.** `{ outcome: "started", run }` settles when the
55
+ Orchestrator accepts the run; the watcher goes round again. `wait: true` settles
56
+ `{ outcome: "settled", run, status, output }` when that run ends, `status` in the store's own words (`done`, `failed`,
57
+ `error`, `cancelled`, `lost`, `drifted`, [ADR-0025](0025-cancel-ends-a-run-stop-parks-it.md)) and `output` the callee
58
+ Machine's (for `failed`, the Failure's `{ reason, detail }`,
59
+ [ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)), which `GET /runs/<id>`
60
+ serves for a run that is no longer live from this decision on (a live run serves `active` and no output). The slot's
61
+ `timeout` bounds the whole Effect, the start alone or the start and the wait: 30 s by default without `wait`, 10 min
62
+ with it, counted from the intent row's first write, so a restore never starts the bound over, and armed again in
63
+ steps, so a bound past Node's 24.8-day timer holds. A run still live when it runs out settles `status: "live"`, the
64
+ status the run had when the Effect stopped waiting: in-process the run host says it; in the Instance Effector the last
65
+ poll must have read the run live, and a wait whose last poll got no answer is `unknown`. In-process the run host tells
66
+ the wait through a settle hook keyed by the run id, not by its hosting, so a caller restored before its callee hears
67
+ it when the callee settles, and hears `lost` or `drifted` when restore marks it so; foreign, the Instance Effector
68
+ polls `GET /runs/<id>` every 5 s as a long-running Effect, which it keeps across an Orchestrator restart already, and
69
+ a DELETE of the Effect ends that poll and never touches the run. No callback from the callee: a callback needs the
70
+ callee to hold its caller's address and a token for it, which inverts the trust. A door refusal is OUTPUT,
71
+ `{ outcome: "refused", status, report }`, with the door's status — 400 for bad input, 404 for a name the current
72
+ Assembly lacks, 409 for an unrunnable named Chart or a key another Workflow's run holds, 422 for a Chart the door
73
+ refuses, 413 for a start over the bytes an Effector reads, 0 when the Effector refused it before anything was sent —
74
+ as a Chart refusal is ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)): the body may have come from a model,
75
+ and the report must be able to reach it through the Chain. A target no replica could reach is
76
+ `{ outcome: "unreachable" }`, output, as an `http` Effect's is (ADR-0067): nothing was started. The invoke's error is
77
+ kept for defects: no run host or Effector, input that fails the slot's schema, a refused or malformed wire.
78
+ - **The two runs are independent.** Cancelling the caller never cancels the callee; the caller's end never ends it;
79
+ leaving the state stops a wait and never the run; the callee's Workspaces are its own. Temporal's parent-close policy
80
+ (`TERMINATE` by default, `REQUEST_CANCEL`, `ABANDON`) exists because a child there is both a run and a child, and its
81
+ default bites on every continue-as-new of a long-lived parent; here the coupled case is `import`, so there is no
82
+ policy. The link is a record. The callee's row carries `dispatchedBy { run, slot, effect, depth, instance? }`: the
83
+ caller's run and slot, the caller's ledger row id (the idempotency key, below), the dispatched run's own depth in the
84
+ chain, one below its caller and stated by the caller, so one meaning on the wire, in the binding and on the row, and
85
+ for a foreign caller its Orchestrator's name as opaque text — its namespace, never an Instance ID, which names an
86
+ Agent's conversation. The open band carries the routing (`run`, `slot`, `depth`, `instance`) and never the key, which
87
+ names the caller's ledger and stays behind the token. The caller's ledger row carries the run it started, written to
88
+ the intent row the moment the start's answer names it and kept past an abort, and the caller's status lists them as
89
+ `dispatched [{ slot, run, at }]`, the chain's other direction. `jr2 runs` carries `dispatchedBy` after the id,
90
+ `jr2 status` prints `dispatched by:` and `dispatched:` lines, and the Console shows both. The word is **dispatched
91
+ by**. The callee reads nothing of its caller from its input: a Machine that wants to tell its callee something passes
92
+ it as input like any caller.
93
+ - **A chain of dispatches is unbounded.** A run may dispatch a Workflow that dispatches another, or itself: a run that
94
+ dispatches its own successor and ends is Temporal's continue-as-new, the honest way to keep a watcher's snapshot
95
+ small, and a depth ceiling would refuse it. A runaway chain is a resource problem the cluster owner's quota and
96
+ [ADR-0064](0064-a-workspace-waits-for-capacity.md)'s capacity wait bound; the depth on the row makes it readable. A
97
+ depth ceiling is named and deferred beside ADR-0070's spend ceiling.
98
+ - **At-most-once across the wire, by construction.** ADR-0067 sends a start once where it may have arrived, and a
99
+ foreign start whose answer was lost had an unknown outcome. The ledger row's id rides every start as an idempotency
100
+ key, out of band — the `jr2-dispatched-by` header, the `dispatchedBy` record as JSON, never a body field the callee's
101
+ door would have to reserve — and `POST /workflows/<name>/runs` asks the key before anything else, so a re-sent start
102
+ answers its run even when the name would be refused now: 201 with the run for a new one, 200 with
103
+ `{ runId, instanceId, existing: true }` for a repeat, 409 when a run of ANOTHER Workflow holds the key (the caller
104
+ asked for one Workflow and would wait on a run of another), 400 when the header does not parse, and 500, never 404,
105
+ for any other throw, since a foreign caller reads a 404 as "nothing was started" (ADR-0033 as amended). The key lives
106
+ in the callee's blob, and the store copies it into a `dispatch_effect` column of its own at every save and indexes
107
+ that, not an expression index over `json_extract`: SQLite's JSON parser refuses a blob nested about a thousand deep,
108
+ and such an index would fail that row's save and every later open of the volume. A run restore marks `lost` keeps its
109
+ row and its blob, so its key keeps answering; the weekly sweep
110
+ ([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-a-week.md)) removes the key with the row, and a re-send
111
+ after it starts a new run: known, and not guarded. In-process the ledger is consulted directly, through the same start
112
+ under the same key, with the starts in flight held so two arrivals of one key before the first save are one run. So a
113
+ dispatch is idempotent whatever its author writes: its intent row says `idempotent: true`, a restore that cannot tell
114
+ whether its start arrived sends it again under its key with what is left of the bound, the Instance Effector sends a
115
+ start whose answer was lost again under its key with backoff until the bound, and the Effector's rollout grace is
116
+ never stretched by a dispatch's wait. Once the answer names the run, the intent row carries it, the Effector's
117
+ `running` state carries it on the wire, and a restore with it starts nothing: it settles `started` at once, or resumes
118
+ the wait. Without it a watcher double-starts a run after a restart, which a human notices as two pull requests. What
119
+ it does not guard: a door at a Kit version before this decision ignores the header, so a re-send to it starts a second
120
+ run, and neither the Effector nor the preflight can tell. A capability check is named and deferred.
121
+ - **`jr2 up` checks what it can see, and the severity follows who owns both sides.** For a same-Instance name the walk
122
+ resolves it in the Assembly and REFUSES a miss, naming the fix: on the host, among the Machines and the Chart files,
123
+ and a slashed name whose wrapper exists but whose Chart the host cannot see is a warning there, since a runtime Chart
124
+ lives on the Orchestrator's volume alone; at the publish door, among all three, and there the miss refuses the
125
+ publish. A dispatch of a runtime Chart under a wrapper the same publish adds takes two publishes, the wrapper first,
126
+ then the Chart's PUT, then the dispatch, and the refusal says so. A Workflow is named by its filename stem, so a stem
127
+ outside `[a-z0-9-]` is refused at load, naming the rename: `jr2 run`, the door's path and a `dispatch` slot name one
128
+ set of Workflows. ADR-0067's "a pick param nothing reads" check runs at `createMachine`, before any callee is known,
129
+ so it reads the slot's `input` schema's keys, and with no schema it reads nothing and skips; the Chart door, for a
130
+ lent dispatch with no schema, takes any object. The walk then compares the slot's `input` schema, if given, with the
131
+ callee's door and REFUSES a mismatch. For a foreign target it does `GET /workflows/<name>` from the host with NO
132
+ token, since the route is open band ([ADR-0014](0014-observation-is-open-run-state-is-not.md)) and the token is the
133
+ Effector's to carry, compares the served JSON Schema with the slot's schema the same way, and WARNS on no reach, a
134
+ miss, a mismatch, an answer that is no Workflow detail or a redirect, never refuses: the other Instance may be down,
135
+ reached only from inside its cluster, or published later, and a refusal would couple two Instances' publish order. The
136
+ token's own refusal is a run's to answer. **A match is compatibility, one direction**: every required field of the
137
+ door is in the slot's schema, required there too, with a compatible type, within the door's `enum`, `const` and
138
+ bounds, and every field of the slot's schema exists in the door, since a plain zod door strips the rest silently; the
139
+ slot's schema is read as what the driver sends (`io: "output"`, its defaults filled), the door as what it takes. It
140
+ compares within the JSON Schema subset ADR-0070 names (`type`, `properties`, `required`, `items`, `enum`, `const`, the
141
+ bounds); a schema that uses something outside it (`oneOf`, `$ref`) is reported "not compared", never guessed at, a
142
+ warning at `jr2 up` which the publish door, with no warnings channel, drops. **A dispatch resolves at run time against
143
+ the current Assembly**, as `jr2 run` does: a dispatched run is a new run and starts under the Assembly current then
144
+ ([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)), which may be newer than
145
+ the caller's. A publish that keeps the caller and drops the callee is refused by the walk, as above; one that drops
146
+ both leaves a live caller to answer `refused` with the 404's report when it dispatches; pinning a dispatch to the
147
+ caller's Assembly would run a Workflow the Instance no longer publishes.
148
+ - **Tests.** The mechanics tier (socket-free, the stub Harness; `features/dispatch.feature`) owns the in-process path:
149
+ the ledger row and the listing both ways, `wait` and the served output, restore mid-wait with one run, cancel of the
150
+ caller with the callee live, the 400 and the 404 as output, the publish refusal of a miss, and two HTTP starts under
151
+ one key as one run. One `@kind` scenario has the Instance dispatch to ITSELF by url and token, so the Instance
152
+ Effector's foreign path, the key and the poll run with no second cluster, across an Orchestrator restart. No `@model`
153
+ claim: the four new pick answer texts are rendered by the generic path every Effect's answer takes.
154
+
155
+ ## Considered options
156
+
157
+ - **Keep ADR-0049 and compose.** `import` plus invoke, a `workspace()` nested where the callee needs its own Sandbox,
158
+ and `pool()` for fan-out. It is still the answer for a coupled child. Rejected for this use because the callee cannot
159
+ have its own row, its own cancel, a life past the caller's, or another Orchestrator.
160
+ - **A bare `http` Effect against the door.** Works today for a foreign Instance. Rejected: it puts the Instance token in
161
+ the author's hands for the same-Instance case, gives `jr2 up` nothing to check, and has no idempotency key.
162
+ - **A sub-run as a child actor with its own id** (Temporal's child workflow). Rejected as ADR-0049 did: cross-run
163
+ correlation and cancel propagation inside one actor tree, for a run that must be independent anyway.
164
+ - **A callback from the callee for `wait`.** Rejected above: it inverts who holds the token.
165
+ - **A depth ceiling, or a static refusal of self-dispatch.** Rejected above: both refuse continue-as-new.
166
+ - **The body wrapped as `{ input, options }`.** Rejected: it taxes every dispatch now for a run-start option the door
167
+ has never had.
168
+ - **One `dispatch()` slot that takes the name as input.** Rejected: the name is authority and stays a constant.
169
+
170
+ ## Consequences
171
+
172
+ - ADR-0049's "no sub-run" is restated, not reversed: a nested Machine is reached by `import`, never by name; a run that
173
+ starts a run is a `dispatch`, by name, and the two are different uses. ADR-0067 gains the kind and a fourth Placement,
174
+ `orchestrator`. ADR-0068's Instance Effector gains the `dispatch` kind, its stop route stops a dispatch's wait too,
175
+ and its `running` state carries the run. ADR-0033's door gains an idempotency key.
176
+ - The GLOSSARY's **Effect** gains "or starts a run", and `_Avoid_: sub-run, child run, nested run, spawned run` for the
177
+ relation, which is **dispatched by**; its **Effector** and **Instance Effector** entries name the in-process exception
178
+ and the new kind.
179
+ - `RunStatus` gains `output` for a run that is no longer live, `dispatchedBy` and `dispatched`; `RunObservation` the
180
+ routing of both, without the key.
181
+ - An Open body may lend a `dispatch` slot as it lends any Effect (`lends.effects`), so a Chart can start a run of a
182
+ Workflow its wrapper chose; the Chart fills the body only.
183
+ - Deferred, named: a start-only token; a depth ceiling; a capability check for a door that ignores the key.
@@ -0,0 +1,144 @@
1
+ # The docs ship inside the Kit, and an Instance's `AGENTS.md` indexes them
2
+
3
+ Most Machines will be written by an Assistant, not by a human: the human asks for a Machine and reviews its graph. No
4
+ model's training data holds jr2's API, so an Assistant needs the docs for the exact Kit version the Instance resolves,
5
+ and it must find them without being told to look. The docs-site grill of 2026-10 settled where those docs live and how
6
+ an Assistant reaches them. Its evidence is Vercel's evals on APIs that are not in training data: a compressed docs index
7
+ inline in `AGENTS.md` passed 100% of cases; skills passed 79% at best, because the agent did not invoke the skill in 56%
8
+ of cases; no docs passed 53% ([Vercel](https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals)).
9
+ Next.js ships the same design: its docs as Markdown inside the `next` package, and a managed block in the project's
10
+ `AGENTS.md` ([Next.js](https://nextjs.org/docs/app/guides/ai-agents)).
11
+
12
+ ## Decision
13
+
14
+ - **One Markdown source, three outputs.**
15
+ - **The bundle.** `@jr2/orchestrator` ships `docs/`: every page of the sitemap, the generated Reference, the Glossary
16
+ and the Decisions, as plain Markdown. No site-generator syntax reaches it. It is the main channel for an Assistant.
17
+ Each page is at its slug, such as `concepts/gates.md`, and each ADR is at `reference/decisions/<file>`. An Assistant
18
+ reads a page with its file tool, which cuts off a long file. So the Authoring surface is two parts:
19
+ `reference/authoring-surface.md`, a contents page that gives each name its kind and the first sentence of its TSDoc,
20
+ and `reference/authoring-surface/<anchor>.md`, one page for each export and for each type that a signature names.
21
+ One page of all the names is about 200 KB, and the contents page is less than 25 KB. No line of the contents page or
22
+ of an entry page is longer than 1,000 characters, so the type plumbing is a list, not a paragraph. When the contents
23
+ page passes 25 KB, first make the long first sentences shorter; if it still passes, give each group of the contents
24
+ page a page of its own, and make the contents page a list of the groups. The first sentence ends at a stop, not at a
25
+ colon, because the words before a colon often tell nothing alone ("An invoke."). A link to an entry goes to its
26
+ page. Each of the other generated Reference pages (CLI, config, machines, mechanism events, HTTP API) stays one
27
+ page. Each is less than 30 KB, the size that one read of a file tool shows, with one exception: the HTTP API page is
28
+ about 50 KB. It is one page because its routes come first and are less than 30 KB, so one read shows every route;
29
+ the types of the bodies come after the routes, and an Assistant reads one of them with an offset or by its heading.
30
+ When the routes pass 30 KB, give the types a page of their own. The alternative, a split of the HTTP API page now,
31
+ is rejected because a route and the types it names are then on two pages, and one read shows all routes already. The
32
+ Glossary and the Decisions are not generated, and this rule does not hold them: an ADR can be about 50 KB, and an
33
+ Assistant reads the one ADR that a page links to.
34
+ - **The `AGENTS.md` block.** A compressed index of the bundle, and an instruction in five parts. jr2 is newer than
35
+ your training data, so read the bundled docs before you write a Machine or config. Start with the guide "Write a
36
+ workflow" and the contents page of the Authoring surface. Read one page with each call of the file tool. Run
37
+ `npx --no jr2 check` before you give a change to the user. A final state means that the work succeeded; when it did
38
+ not, invoke `fail()`, and do not throw to fail. The Failure rule is in the block because no refusal can teach it:
39
+ the walk cannot see that a final state means "this did not succeed", or that a promise throws only to fail, and in
40
+ the eval of [ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md) three of the
41
+ four trials wrote a failure that ended `done` or `error`. The two entry pages in one read are more than about 30 KB,
42
+ and a file tool then shows only the start of the output. The check is in the block because `npm run typecheck` loads
43
+ no module. Thus it does not find a refusal of `createMachine`, `defineEvent` or `workspace()`. An Assistant that may
44
+ not run `jr2 up` gets those refusals only from `jr2 check` (ADR-0009). The command has `--no` because npx with no
45
+ TTY installs a missing package with no prompt. The package `jr2` on the npm registry is not jr2. With `--no`, npx
46
+ stops when the Instance has no local `jr2` bin, for example before `npm install`. `npm run check` also does not
47
+ fetch, but an Instance from an older scaffold has no `check` script. The index names each file of a directory, but
48
+ the Decisions folder and the Authoring surface folder each have one entry, which names the contents page of the
49
+ folder. Thus the line stays short.
50
+ - **The site**, on GitHub Pages. HTML for humans, and every page also at a `.md` URL, plus `llms.txt`, for tools that
51
+ read over the network.
52
+ - **The docs ride `@jr2/orchestrator`, not a package of their own.** The Kit version _is_ the `@jr2/orchestrator` the
53
+ Instance resolves, so the bundle matches the code the Assistant writes against by construction. There is nothing else
54
+ to pin.
55
+ - **`jr2 init` writes the block, and `jr2 up` keeps it current.** `init` writes `AGENTS.md` with the block between two
56
+ markers, and a `CLAUDE.md` that holds `@AGENTS.md`, because Claude Code reads `AGENTS.md` only when no `CLAUDE.md`
57
+ exists. `init` takes the block from the bundle of the CLI's own `@jr2/orchestrator` when that is the version it pins;
58
+ without that bundle it leaves the markers empty. An `AGENTS.md` or a `CLAUDE.md` that exists stays as it is, and
59
+ `init` says what to add to it. `up` rewrites the text between the markers when it differs from its Kit version's
60
+ block, changes nothing outside them, and prints one line: `AGENTS.md: jr2 block updated for <version>`. It does this
61
+ before the cluster work, so a converge that fails still updates the block. `up` is the verb because every upgrade ends
62
+ in `up`. A folder without the markers has opted out: `up` never creates the file or the markers, and no config key
63
+ exists for it. A broken marker pair is reported, and `up` does not change the file.
64
+ - **The Reference is generated from the code wherever the code holds the facts.**
65
+
66
+ | Page | Source |
67
+ | ------------------------------------------------------- | ------------------------------------------------------ |
68
+ | Authoring surface: a contents page, and a page per name | TSDoc on the exports, exactly the root's list |
69
+ | `@jr2/machines` | TSDoc on the exports |
70
+ | `jr2.config.ts` | TSDoc on the `JR2Config` fields |
71
+ | CLI | a structured command table, which also prints `--help` |
72
+ | HTTP API | the route table in `http.ts`, made data |
73
+ | Mechanism events | the code that defines them |
74
+ | Glossary, Decisions | `GLOSSARY.md` and `docs/adr/`, rendered as they are |
75
+
76
+ The TSDoc on an Authoring surface export is written for the author: what it does, what it refuses, what to read next,
77
+ and one example. Maintainer reasoning goes in `//` comments in the body. The packages ship their `.ts` source, so an
78
+ Assistant that follows an import into `node_modules` reads the same text the site shows. The Authoring surface is
79
+ exactly what `@jr2/orchestrator` exports from its root: what the kit's packages share is internal even where it can be
80
+ imported, and `@jr2/machines` is not part of it, because it ships Machines, not names to author with.
81
+
82
+ - **`docs:build` assembles the bundle at pack time.** Its inputs are the hand-written pages in `docs/site/`, the
83
+ generated Reference, `GLOSSARY.md`, and `docs/adr/`. It runs in `@jr2/orchestrator`'s `prepack` and before the site
84
+ build. Its output is gitignored, and no generated file is committed. A link that leaves the docs set becomes a GitHub
85
+ URL at the release tag; a link that resolves nowhere fails the build. The `@dist` tier asserts that the packed bundle
86
+ exists and that the `AGENTS.md` from `jr2 init` indexes it.
87
+ - **The site describes the latest release and deploys on its tag**, in a `pages` job in `release.yml` that needs the
88
+ release job, so a failed gate deploys nothing. It does not wait for the staged packages' approval
89
+ ([ADR-0055](0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md)).
90
+ - **VitePress builds the site, from the pages as they are.** The source format is VitePress's own Markdown, so a page
91
+ renders on GitHub and in the site, and an error names the file that a writer edits. `docs:build` accepts four forms of
92
+ it, and makes each one plain for the bundle: an aside, the provider tabs, a code group, and a `<<<` code import. It
93
+ refuses every other form, so no generator syntax reaches the bundle. The provider tabs are `::: tabs key:provider`
94
+ from `vitepress-plugin-tabs`, not VitePress's `::: code-group`: a provider tab holds prose as well as code, and the
95
+ plugin keeps the reader's choice on each page, which a code group does not. `docs:build` refuses tabs with another
96
+ key, and a `<<<` of a file outside `docs/site/examples/`, which `pnpm typecheck` checks. A `mermaid` fence is a theme
97
+ component that imports mermaid only on a page with a diagram. The site has local search, a `sitemap.xml`, and the
98
+ `.md` URL of each page in a `<link rel="alternate">`.
99
+
100
+ ## Considered options
101
+
102
+ - **Skills.** Rejected as the channel for API knowledge: the Vercel evals above, and an agent decides whether to invoke
103
+ a skill, where it always reads `AGENTS.md`. Skills stay possible for a multi-step job, such as an upgrade.
104
+ - **A docs MCP server.** Rejected: one more process to run for text that can sit on disk. MCP is for acting on a running
105
+ system, not for reading docs.
106
+ - **The site and `llms.txt` only.** Rejected: the site describes the latest release, and an Instance can pin an older
107
+ one. An Assistant also fetches over the network inconsistently, and some run without network.
108
+ - **A separate `@jr2/docs` package.** Rejected: the Instance must install it and pin it to the Kit version, and a
109
+ mismatch is silent. Inside `@jr2/orchestrator`, it cannot mismatch.
110
+ - **The Authoring surface as one page.** Rejected: an Assistant's file tool cuts it off, so it reads the first entries
111
+ and not the rest.
112
+ - **An entry page named by its export, such as `Customize.md`.** Rejected: `customize` and `Customize` are both exports,
113
+ and on a file system that ignores case (macOS, Windows) the two are one file. A page is named by its anchor, which is
114
+ lower case and starts with `type-` for a type.
115
+ - **A hand-written Reference.** Rejected: it drifts from the code, and the Reference is what an Assistant trusts most.
116
+ - **Copies of the Glossary and the ADRs committed inside the package**, kept equal by a drift check. Rejected: two
117
+ copies of one text, to avoid one pack-time step.
118
+ - **`AGENTS.md` written once by `init`.** Rejected: it goes stale at the first upgrade. **A `jr2 docs` command to
119
+ refresh it** is rejected too: a step that someone must remember.
120
+ - **Deploy when npm serves the version** — an hourly job that compares `npm view` with the deployed version. Rejected:
121
+ more machinery to close a window of minutes.
122
+ - **Astro Starlight.** Built to the same bundle, and compared with VitePress on the same pages. It had better search and
123
+ a lighter page, which VitePress gets with a small search fix and a mermaid component. Rejected: its tabs and code
124
+ imports are MDX components, so 28 pages become `.mdx` that GitHub shows as source text, and a page carries more than
125
+ twice the generator lines.
126
+
127
+ ## Consequences
128
+
129
+ - `jr2 up` writes into the Instance folder, and only between the markers.
130
+ - `@jr2/orchestrator` gains its first pack-time step. Its source still ships as `.ts`; only `docs/` is assembled.
131
+ - The Orchestrator's Kit image packs `@jr2/orchestrator` with `npm pack --ignore-scripts`, and its build context drops
132
+ `docs/`: the `prepack` needs pnpm and the whole repo, which the image stage has not, and the Orchestrator's process
133
+ never reads the bundle. So the image holds no docs, and a local `docs:build` does not move its tag
134
+ ([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)).
135
+ - The tarball grows by the docs, most of it the ADRs (about 0.7 MB of text today).
136
+ - A TSDoc edit on the Authoring surface is a docs edit. `--help` and the CLI Reference come from one table, so they
137
+ cannot disagree.
138
+ - A docs fix goes live with the next release, not on merge.
139
+ - A rejected stage leaves the site describing a version that npm does not serve, until the next tag. Accepted: the
140
+ window is short, and a rejected stage is rare.
141
+ - The bundle is plain Markdown, so a change of the site generator changes `build/vitepress.ts` and the pages' forms, and
142
+ nothing that an Assistant reads.
143
+ - VitePress 1.x is on Vite 5, with dev-server advisories that the site cannot fix. The site is static, so they reach
144
+ only `docs:dev`. A move to VitePress 2 is a later change.
@@ -0,0 +1,122 @@
1
+ # A Machine fails a run with `fail()`, and an uncaught Failure ends it `failed`
2
+
3
+ A run ended `done` when its Machine got to a final state, and that did not say that the work succeeded. No Machine could
4
+ end a run as a failure. An eval in which an Assistant writes jr2 Workflows showed the gap: three of the four
5
+ ticket-to-pr trials wanted a failure that is not `done`. Two threw from a plain `fromPromise` ("the planner changed the
6
+ code", "the push failed"). The run then ended `error`, with the message in `fault`, no output and the Sandbox kept, so
7
+ an operator could not tell a kit bug from a failed ticket. One trial wrote `failed` and `lost` final states with the
8
+ comment "the run faults rather than lie about a decision", and that run ended `done`. The fourth put a
9
+ `failed: "<reason>"` variant in its output union, which every reader of the run then had to know about.
10
+
11
+ ## Decision
12
+
13
+ - **A Failure is the end that a Machine chooses for work that did not succeed.** A Machine makes one with the Part
14
+ `fail()`: `actors: { fail: fail() }`, invoked as `src: "fail"` from any state, at any depth. Its input is
15
+ `{ reason: string; detail?: unknown }`: `reason` is text for a person, and `detail` is optional JSON for a machine
16
+ reader. The Part throws an error with a brand. The brand is a registry key (`Symbol.for`), because an Instance can
17
+ load a second copy of the kit, and both copies must read the same brand. The kit does not export it, and an author
18
+ makes a Failure only with `fail()`. It is a Part and not an Effect, because it does no work outside the Machine. The
19
+ slot name is the author's; the docs use `fail`, never `abort`, which is a Settlement's word and the wire's.
20
+ - **A Failure goes up by xstate's own error rules.** It goes from the state that invokes `fail` to each parent Machine
21
+ in turn, and an `onError` on the way catches it, as a `catch` does. `isFailure(error)` tells a Failure from a defect
22
+ and narrows it to the payload:
23
+ `onError: [{ guard: ({ event }) => isFailure(event.error), target: "replan" }, { target: "defect" }]`. In a Chart, the
24
+ same guard is `jr2.failure`: every Chart has it, as every Chart has `jr2.sentBy`, because it is a predicate and gives
25
+ no capability.
26
+ - **An uncaught Failure ends the run `failed`.** `failed` is a Run status of its own, beside `done` and `error`. `fault`
27
+ stays empty, and the run's output is the payload, not a value of the Machine's output type. `jr2 status` and the
28
+ Console show the reason, and the Console gives `failed` a color that is not the red of `error`.
29
+ - **A Failure deletes the Sandbox.** A Failure is a chosen end, as `done` is, and each kept Sandbox holds its whole Size
30
+ ([ADR-0060](0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md)) until its Lease lapses. `workspace()`
31
+ catches a Failure from its body: `running` gets `onError` guarded by `isFailure` to a `failing` state, which runs the
32
+ same `destroy` as `teardown` and then invokes `fail` again with the same payload. So `workspace()` still owns the
33
+ Sandbox's life ([ADR-0012](0012-workspace-wrapper-machine.md)), a caught Failure leaves no Sandbox behind, and the
34
+ parent gets the Failure unchanged. A defect still goes up with no `onError`, and its Sandbox stays as evidence. The
35
+ host deletes every Sandbox of a run that ends `failed`, after the stop, as `cancel` does
36
+ ([ADR-0025](0025-cancel-ends-a-run-stop-parks-it.md)); after the wrapper's own delete, that is only a backstop. When a
37
+ Machine above catches the Failure, the host stops the children of the Machine that the Failure ended, and after the
38
+ stop it deletes their Sandboxes, because a stop skips `teardown`. It does not delete a name that a live Workspace of
39
+ the run holds then, and nor does a Workspace that was stopped in `placing`
40
+ ([ADR-0064](0064-a-workspace-waits-for-capacity.md)): a parent that invokes the same Machine again gives the new
41
+ Workspace the same invoke id, and so the same Sandbox name. That new Workspace takes over the Sandbox and its
42
+ Worktrees, as a restore does, with the work of the failed attempt still in it. To keep the work for a person to read,
43
+ the Machine waits at a Gate before it fails.
44
+ - **`jr2 run` exits 0 only for `done`.** It exits 1 for every other end: `failed`, `error`, `cancelled`, `lost`,
45
+ `drifted`, and a run that was parked before it settled. Exit code 2 stays the CLI's code for a usage error. The status
46
+ line on stderr tells which end it was, and `jr2 status --json` tells a script.
47
+ - **A `dispatch` parent reads `failed` as a status.** `settled.status` gets `failed`, on the in-process path and in the
48
+ foreign `DispatchOutput` shape ([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)), and `output` is the
49
+ payload.
50
+ - **Each state that can fail the run is static.** The walk finds each state that invokes a `fail()` slot, also in a
51
+ nested Machine. `jr2 up`, `jr2 check` and the Machine doc list them, as they list Gates and Effects.
52
+ - **A Machine lends `fail` to a Chart as it lends `gate`**: `lends: { fail: true }`
53
+ ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)). The Machine decides if a Chart can end its run as a
54
+ Failure, and it can catch one with an `onError` on its Open body state.
55
+ - **A plain throw stays a defect.** Logic that throws ends the run `error`, as before. jr2 cannot see in the walk that a
56
+ promise only throws, so the docs say it: to fail, invoke `fail`; never throw to fail, and a final state means `done`.
57
+ - **A decision of a person fails the run only when it ends the run.** The rule is about the state where the run ends. A
58
+ `reject` that sends the work back is a usual route. A decision that ends the run, such as a `reject` at a Gate or a
59
+ `roll_back`, goes to a state that invokes `fail` with the reason of the person, not to a final state, and
60
+ `{ outcome: "rejected" }` leaves the output unions. A Machine whose job is the decision itself, such as a triage where
61
+ "no" is a correct answer, keeps a final state and puts the decision in its output.
62
+ - **The shipped `task` Machine fails when its Sandbox is lost.** Its `lost` state invokes `fail` and is no longer final,
63
+ and `outcome` leaves `TaskOutput`, which is `{ branch }`. A parent that read `outcome: "lost"` now catches the
64
+ Failure. `pool()` is such a parent: it keeps a worker's Failure in `failures` and goes on, so that one lost worker
65
+ does not delete the Sandboxes of its siblings ([ADR-0017](0017-pool-over-a-source-port.md)). When it ends with
66
+ failures, `drained` or `deadlocked`, it fails the run with the reason `"<n> of <m> items failed"` and the detail
67
+ `{ status, items, failures }`, unless its option `failures` is `"report"`: then it ends `done`, with the failures in
68
+ its output. The docs examples whose final states mean "this did not succeed" change in the same way.
69
+ - **A restart replays a Failure.** The state that invokes `fail` persists as any state does
70
+ ([ADR-0007](0007-durable-machine-state.md)), and on restore `fail` runs again and throws again.
71
+
72
+ ## Considered options
73
+
74
+ - **A failure is only the Workflow's output** (the fourth trial's shape). Rejected: the `jr2 run` exit code, the Sandbox
75
+ decision and the Console must react the same way for every Workflow, and none of them can read a Workflow's own output
76
+ shape. Each CI script would parse the output of each Workflow.
77
+ - **`jr2 run --fail-on <path>`.** Rejected: it puts a fact of the run in one client, so the Console and `dispatch` do
78
+ not see it.
79
+ - **A tag, `run.failed`, on a top-level final state.** Plain xstate and static, and it was this ADR's first form.
80
+ Rejected: only the root's final state can give the status, so each level between the failure and the root must carry
81
+ it. xstate's done event carries only the output, so `workspace()` would need a second, tagged final state and a fact
82
+ in its context; the Chart wrapper would need a new outcome; each nested Machine would map its child's output to a
83
+ tagged final state of its own. A throw needs none of that, because xstate already sends an unhandled error up through
84
+ each parent.
85
+ - **An output shape that jr2 reads**, such as `{ failed: reason }`. Rejected: it gives jr2's meaning to a key in the
86
+ author's own data, which is hidden mechanism ([ADR-0015](0015-authoring-surface-absorbs-the-mechanism.md)), and it
87
+ takes part of the output type from the author.
88
+ - **The unbranded throw** that the trials wrote. Rejected: it ends `error` with no output, and nobody can tell it from a
89
+ defect.
90
+ - **A separate exit code for `failed`.** Rejected: CI needs to know that the run did not succeed, and the status tells
91
+ which end it was.
92
+ - **A typed `fail<T>()`** with an input schema. Not now: it adds a type parameter to the Authoring surface, and no trial
93
+ needed a typed `detail`.
94
+ - **`fail` in every Chart, with no lend.** Rejected: the Machine decides which ends a Chart can choose, as it decides if
95
+ a Chart can wait at a Gate.
96
+ - **A failed run keeps its Sandbox**, as a fault does. Rejected: a fault keeps it because nobody chose that end; a
97
+ Failure is chosen, a CI loop of failed tickets would fill the cluster, and a Gate keeps the work when the author wants
98
+ it.
99
+ - **Another word than `failed`**, because a Settlement has a `failed` value too. Rejected: `failed` is the word CI and
100
+ people use, the two are about different things (a run and a Submission), and each glossary entry names its subject.
101
+
102
+ ## Consequences
103
+
104
+ - **`onError` carries two things**: a defect or a Failure, which `isFailure` separates
105
+ ([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)). A broad
106
+ `onError: "retry"` catches a Failure too, as a broad `catch` does.
107
+ - **A failed run's output has the kit's shape**, so `DispatchOutput` has a branch for `failed` and a parent reads the
108
+ payload there.
109
+ - **An uncaught Failure stops the tree** as an error does: exit actions and `teardown` above the failing state do not
110
+ run, and the host does their cleanup.
111
+ - **`done` now means that the work succeeded**, by the convention that the docs teach: a Machine that did not succeed
112
+ fails.
113
+ - The e2e and `@model` settle loops treat every status that is not `live` as settled (`features/lib/run-settled.ts`), so
114
+ a `failed`, `cancelled` or `lost` run does not poll until the timeout. The Workflow fixtures of the `@kind` and
115
+ `@model` tiers that end in a `faulted` or `lost` final state invoke `fail`. A trial's note prints the Failure of a
116
+ `failed` run: `EXPLAINED_STATUSES` in `features/lib/model-status.ts` lists `failed` with the other statuses that it
117
+ explains, and `model-finished.ts` counts only `done` as finished. The Chart fixtures keep their final states: no
118
+ wrapper that runs them lends `fail`, and they test the door and the routing, not how a run ends.
119
+ - The change touches what a model reads (the docs, the AGENTS.md index, the fixtures' Machines), so the `@model` tier
120
+ runs before it is done
121
+ ([ADR-0066](0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md)), and the agents-md
122
+ eval runs against it.
@@ -0,0 +1,76 @@
1
+ # An unrouted mechanism event is refused at load and faults the run
2
+
3
+ jr2 sends `agent.fault` to the Machine that invoked an Agent when the Agent's Turn cannot go on
4
+ ([ADR-0016](0016-agent-turn-mechanics-are-internal.md)), and `workspace()` sends `workspace.lost` to its body when the
5
+ Sandbox is gone ([ADR-0012](0012-workspace-wrapper-machine.md)). Both go by `sendBack`, and xstate drops an event that
6
+ no active state handles. So a Machine that does not route `agent.fault` waits for ever in the Agent's state with a dead
7
+ Agent, and a body that does not route `workspace.lost` shows healthy on the feed with no Sandbox
8
+ ([ADR-0022](0022-observation-is-a-level-triggered-feed.md)). Six of the 12 trials of an eval in which an Assistant
9
+ writes jr2 Workflows did not route `agent.fault`. Each of those Machines loaded, and each could wait for ever.
10
+
11
+ ## Decision
12
+
13
+ - **The walk refuses a Machine with no route for a mechanism event.** It refuses a state that invokes an Agent, or an
14
+ Open body that lends an Agent or allows a model, when that state and all its ancestors have no `agent.fault` route,
15
+ and a state of a `workspace()` body that the body can stay in when that state and all its ancestors have no
16
+ `workspace.lost` route. The states that a body can stay in are each leaf that is not final, and each compound state
17
+ whose nested final state has no `onDone` on the compound state: the body then stays in the compound state. An event
18
+ goes to the active leaves first, so a route on each leaf, or above it, is a route for the compound state above them. A
19
+ final state of the root ends the body, and a final state of a parallel region leaves the other regions active, so
20
+ neither is judged. One route on the root satisfies each rule. The `agent.fault` refusal is in
21
+ `jr2Setup().createMachine`, beside the lost-output refusal, so `jr2 check`, `jr2 up` and the Orchestrator's load all
22
+ show it. The `workspace.lost` refusal is in `workspace()`, which is the one place that knows that a Machine is a body.
23
+ - **At runtime, a mechanism event that no transition took faults the run `error`.** The run host sees it in the
24
+ inspection stream: a microstep for `agent.fault` or `workspace.lost` with no transitions, in the Machine that got the
25
+ event (for `workspace.lost`, the body, because the wrapper always takes its own transition to forward it). `fault`
26
+ names the event, the state and the event's `reason`. This catches the route that the walk cannot judge: a guard that
27
+ is false when the event comes.
28
+ - **In a Chart, the `agent.fault` route is optional, and the Machine that invoked the Open body routes the rest.** The
29
+ Open body sends an `agent.fault` that no transition of the Chart takes to the Machine that invoked it, unchanged:
30
+ `{ type: "agent.fault", instanceId, reason }`, with the Instance ID of the Chart's Agent
31
+ ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)). It does not step the Chart. That Machine's `agent.fault`
32
+ route takes the event, as it takes the fault of an Agent of its own, and the exit from the Open body's state stops the
33
+ body. A route that does not exit the state keeps the Chart where it is, and the Chart's Agent is gone: the Chart goes
34
+ on only on its other events, or ends at its wall-clock ceiling, as a route with actions only does to a state of a
35
+ Machine. With no route that takes it, the runtime rule above faults the run. So one route on the root of a Machine is
36
+ the route for its own Agents and for the Agents of its Charts. An Agent of a child Machine sends to that Machine. The
37
+ Chart door does not refuse a Chart state with no route, and `jr2Setup().createMachine` does not refuse the Machine
38
+ that a Chart compiles to. Under an Open body that lends no `fail`, a Chart has no correct state for the route: a final
39
+ state means `done`, and a state that waits holds the run until the wall clock. The Chart wrapper, which is TypeScript
40
+ and checked by the walk, decides.
41
+ - **Routed means that the runtime took a transition.** A route with actions only counts, because it can be a deliberate
42
+ `reenter: true` that invokes the Agent again. jr2 does not judge what a route does.
43
+ - **An unrouted mechanism event ends the run `error`, never `failed`.** The Machine did not choose that end, so it is a
44
+ fault and not a Failure ([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)),
45
+ and its Sandbox stays as evidence.
46
+
47
+ ## Considered options
48
+
49
+ - **Drop the event, as before.** Rejected: the run waits for ever and looks live, and nothing tells a person why.
50
+ - **Only the walk.** Rejected: a guarded route that is false at runtime still drops the event.
51
+ - **Only the runtime.** Rejected: half of the trials would load and then fault on the first Agent fault, when the walk
52
+ can tell the author before the first run.
53
+ - **Make `agent.fault` the Agent actor's error**, so that xstate faults the parent when no `onError` handles it.
54
+ Rejected: ADR-0016 and the Chart vocabulary route the event by name, and `onError` already carries defects and
55
+ Failures; an Agent's fault would be a third thing on it.
56
+ - **A Chart's unrouted `agent.fault` ends the Open body as an error**, which the Chart wrapper catches with an `onError`
57
+ on its Open body state. Rejected: that error and a defect of the body are the same plain error, so the Chart wrapper
58
+ cannot fail the run for a Chart Agent's fault without failing it for a defect too, and one Machine routes the fault of
59
+ its own Agent on `agent.fault` and the fault of a Chart's Agent on `onError`.
60
+ - **A mark on that error**, such as `isAgentFault(error)`, with a matching Chart guard. Rejected: a new name on the
61
+ Authoring surface, and still two routes for one event.
62
+ - **Require the route in a Chart too**, with a line of the Chart door. Rejected: under an Open body that lends no
63
+ `fail`, the planner can write no correct target, and the trials of the `chart-refusal` claim wrote a `faulted` final
64
+ state, which ends the run `done`. A route the planner must write also adds a rule to the text that a model reads.
65
+
66
+ ## Consequences
67
+
68
+ - The text "When no state routes it, xstate drops the event, and the run stays in the Agent's state" in the
69
+ `FaultTelemetry` TSDoc (`actor.ts`) and in `mechanism-events.md` changes to this rule.
70
+ - A Machine in the docs, the templates or the fixtures that does not route a mechanism event gets a route, or the walk
71
+ refuses it.
72
+ - A Chart needs no route, so the Chart fixtures and the Charts of the docs keep none that they do not use, and the Chart
73
+ briefing and the grammar's fragment do not show one. Their Chart wrappers route `agent.fault`, or the walk refuses
74
+ them.
75
+ - An Assistant learns the rule from the refusal line of `jr2 check`, so the line names the state, the event and the
76
+ route to write.