@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,436 @@
1
+ # An Agent's turn ends with the state that asked for it
2
+
3
+ An Agent that picks from its menu does not stop. Observed live twice (2026-07-27, `task-with-review` against vLLM). The
4
+ reviewer delivered `review_verdict`, the Machine parked on `humanReview` — and the reviewer went on calling
5
+ `review_verdict` **41 times**, 20 of them inside one 40-second window, its `notes` degrading from a paragraph to
6
+ `"Approved."`. Eleven minutes after the run had stopped listening, both Agents were still generating.
7
+
8
+ Nothing in jr2 ends the submission. `agentRun` awaits `client.settle` — `wait(admission)` — which resolves only when the
9
+ model stops. The delivery transitions the Machine, xstate stops the invocation, and its cleanup runs `abandon()`: the
10
+ registration is destroyed and **local** consumption is aborted. That is deliberate — a host shutdown stops every actor,
11
+ and [ADR-0007](0007-durable-machine-state.md)'s restore needs those durable runs alive to re-attach — but the Harness is
12
+ never told. The surface does not outlive its state ([ADR-0011](0011-workflow-defined-events.md)); the submission does.
13
+
14
+ The Agent has no way to know. This is the receipt, verbatim as the model received it:
15
+
16
+ ```
17
+ Structured content:
18
+ { "deliveryId": "058b1208-5fc9-43db-9057-eda10f871856" }
19
+
20
+ {"deliveryId":"058b1208-5fc9-43db-9057-eda10f871856"}
21
+ ```
22
+
23
+ A UUID, printed twice, against instructions that say it MUST finish by calling the tool and never say when finishing is
24
+ finished. So it calls again — and the retry does not even reach a tool. The Menu's server was rebuilt per request, so a
25
+ call after the state moved on failed at `GET /agents/:iid/surface` and came back as **HTTP 404 on the tool's transport**
26
+ (verified against the running pod: `{"error":"no live surface for agent ..."}`). A connection failure is the most
27
+ retry-inviting thing available, and the model retries, trimming its answer each time.
28
+
29
+ **It is a coin flip, which is what rules out fixing this with words.** In the second run the coder called once and
30
+ settled `completed`, while the reviewer ran away; in the first, both ran away. The same prompt, the same model, the same
31
+ state. A prompt fix cannot be relied on and cannot even be shown to have worked.
32
+
33
+ **And the cost is not only tokens.** [ADR-0012](0012-workspace-wrapper-machine.md) puts coder and reviewer in one
34
+ Workspace; the Machine's states are sequential, but the Agents are not. Throughout `reviewing`, the orphaned coder was
35
+ still live in the worktree the reviewer was reviewing — and still live at `humanReview`, the park that deliberately
36
+ retains the Sandbox so a human can exec in and inspect the diff. In the runs we watched, the orphan only read. Nothing
37
+ prevented it writing. An un-ended turn is a concurrent writer in a Workspace that the Machine did not put there.
38
+
39
+ ## Decision
40
+
41
+ - **A turn ends when the state that asked for it stops waiting.** When an `agentRun` invocation ends, its submission is
42
+ swept — `abort` on the wire (`POST /agents/:name/:id/abort`). `agentRun` is an **invoke**: leaving the state _means_
43
+ "I am no longer interested in this answer", and nothing in the kit offers a detached Agent.
44
+ - **The Orchestrator is the one judge of how a Turn ended, and the sweep says it.** A Submission settles one of four
45
+ outcomes ([ADR-0027](0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md)). `ended` and `aborted` are
46
+ the ends of a Turn. `completed` is a Turn that is still open, and `failed` is a fault. `ended` means that the Turn's
47
+ own pick ended the Turn. The pick may be from this Submission or from an earlier Submission of the same Turn: a nudge
48
+ or a wake starts a new Submission in the same Turn. For example, Submission 1 picks, and its route arms a timer.
49
+ Submission 1 completes, and the nudge admits Submission 2. Then the timer leaves the state: Submission 2 settles
50
+ `ended`. So the sweep carries no Submission of the pick's own. It names the running Submission:
51
+
52
+ | cause | outcome |
53
+ | ------------------------------------------------------------------------------------------------------------------------ | ----------- |
54
+ | the Turn's own pick ended the Turn, and the sweep says `cause: "pick"` | `ended` |
55
+ | something else ended it: a timer, an outside event, another party's event, the run's CANCEL | `aborted` |
56
+ | the Turn's own pick routed to a state that invokes `fail`, and the Failure ended a Machine on the way up | `ended` |
57
+ | a throw in the Machine, or a defect that no state catches | `aborted` |
58
+ | a pick found the Turn over, and no sweep came within the Harness's wait for it (10 s) | `aborted` |
59
+ | it was over before it started: the surface read answered 404 ([ADR-0026](0026-a-turn-that-is-over-is-never-prompted.md)) | `aborted` |
60
+ | it was queued behind a swept Submission, so it never ran | `aborted` |
61
+ | the run returned, and the Turn is still open (the nudge or the wake follows, ADR-0016) | `completed` |
62
+ | the run threw: a provider failure after the retries, a Menu that cannot be read, a Runaway (ADR-0035) | `failed` |
63
+
64
+ **The Harness does not judge.** A pick can find the Turn over: a receipt with `turnComplete`, or a 404 on a pick or a
65
+ poll. Then the Harness ends the run at the step boundary. It sends no other model request, and it blocks every later
66
+ call of that model answer (below). Then the Submission waits for the sweep. The sweep's body says the cause and names
67
+ the Submission: `{ "cause": "pick", "submissionId": … }`. When it names the running Submission, that Submission
68
+ settles `ended`. Every other body settles it `aborted`: no body, a body that does not parse, an unknown cause, or a
69
+ `submissionId` of another Submission (or none). A sweep that lands while the run still runs settles it in the same
70
+ way. A Submission queued behind the running one never ran, so it settles `aborted` for every cause. So the order in
71
+ which the sweep and the Harness's own end land changes nothing. Only the Orchestrator's word decides.
72
+
73
+ **The wait is bounded: 10 s.** The Orchestrator sends the sweep in the macrostep that ends the Turn. That is before
74
+ the pick's own receipt answers, so the sweep lands within one round trip, in milliseconds. 10 s is far above that, and
75
+ above a slow hop or a pause of the Orchestrator's process. It is below every drain window
76
+ ([ADR-0031](0031-menu-only-agents-run-on-the-instance-harness.md)), so a stopping Harness still settles it. While the
77
+ Submission waits, its run has ended and its Agent's actor has stopped. The next Turn on the conversation is admitted
78
+ only after the sweep (the ordered abort, below). So a lost sweep holds two things for up to 10 s: a next Submission
79
+ that is queued on the conversation, and a drain. When no sweep comes, the Submission settles `aborted` with a message
80
+ that says so. The Harness records that the run ended so, and a rebuild after a restart settles it in the same way.
81
+
82
+ **The Orchestrator attributes the exit to its cause.** The Agent's Actor asks the run host what ended the Turn
83
+ (`turnEndedByPick`). It asks as its invocation stops, before its registration goes. The answer is yes exactly when the
84
+ transition that stopped the invocation came from this Agent's own pick. The host reads this off xstate's inspection
85
+ stream and the actor system's scheduler (`cause.ts`). It reads what the Machines did, and never the pick's answer:
86
+ "was the pick's work stopped" (`stopped`) is another question. It keeps two facts:
87
+ - **The cause of each relay.** A cause is a set of Agents' picks. It rides each relay of an event, never the event
88
+ object: xstate relays one object each time a config action with an object literal runs. A relay comes from an
89
+ Agent's pick in four ways:
90
+ 1. It is the pick, in its own send.
91
+ 2. The Machine that invokes the Agent takes it from the work below the Agent's state while the pick is out. These
92
+ are the sources of a reply or of a child's error: a child's output or error, a Gate's answer, a nested Agent's
93
+ pick. A nested Agent's pick is then the cause for both Agents.
94
+ 3. An actor sent it while it took a relay that came from the pick. Examples: a `sendTo(self)`, a `sendParent` from a
95
+ child Machine where the route runs, and a child that answers the route at once. A `raise` is part of the
96
+ macrostep that raised it.
97
+ 4. It is a timer that its Machine armed while it took a relay that came from the pick. A timer that the pick's own
98
+ route armed is the work's own progress. Examples: an `after` of a state that the pick's route entered, and a
99
+ delayed `raise` or `sendTo` of the pick's transition or route. So inline work and the same work as a child
100
+ Machine give one outcome. Each timer keeps its own cause, on its own `setTimeout` of the run's clock. So two
101
+ timers of one id, armed by two relays with no cancel between, each keep the cause of the relay that armed it.
102
+
103
+ Nothing else has a cause. A timer armed before the pick has none, for example a Turn timeout on the Agent's state.
104
+ An outside event, a root Gate's answer, another Agent's pick and a CANCEL have none. A child that the pick's route
105
+ spawns is not the work below: Rest counts invoked children only. So its answer comes from the pick only by rules 3
106
+ and 4: when it answers in the route's own send, or by a timer it armed while it took a relay from the pick (a
107
+ spawned child Machine's own `after`, armed during its init relay). A spawned callback's own `setTimeout` is not a
108
+ timer of the actor system, so its later answer has no cause.
109
+
110
+ - **The exit.** The first microstep that stops the Agent's invocation names the cause of its macrostep. After that
111
+ microstep, one of these is true. The Agent's actor is no longer the child of the Machine that invokes it (the state
112
+ exited, or was re-entered). A child Machine on the way up is no longer the child of the Machine above it. Or a
113
+ Machine on the way up is done: it reached its final state, so xstate stops every child. xstate inspects a microstep
114
+ only after its actions ran. So a microstep that throws is never the exit, and a fault is never the pick's. When a
115
+ Machine above catches the fault of a Machine on the way up, that catch is the exit, and it has no cause, also when
116
+ the pick's route caused the fault. The stop of a run is no event of the Machine's.
117
+ - **A Failure is no fault.** A Failure is the end that a state chose
118
+ ([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)). When a Failure ends a
119
+ Machine on the way up from the Agent, that Machine's error event is the exit, with the cause of that relay. So a
120
+ pick whose route invokes `fail` ends its Turn `ended`, when a Machine above catches the Failure and when the run
121
+ ends `failed`. xstate does not stop the children of a Machine that ends `error`, so the host stops the children of a
122
+ Machine that a Failure ended, after its parent takes the error event. The Agent there then gets its sweep.
123
+
124
+ Which relay an actor takes now is read from the relay order. xstate relays an event to the actor's mailbox
125
+ (`@xstate.event`). The mailbox takes events one at a time, in that order. xstate inspects `@xstate.snapshot` with the
126
+ event when the actor has taken it. An actor's own done or error event is the last relay that it makes with a cause: it
127
+ takes the cause of the relay the actor took then. After it, the actor is finished. A finished actor has no cause, and
128
+ a relay to it, or to a stopped actor, is not queued: xstate drops it. Status alone does not say it. A done Machine
129
+ makes its last deferred sends with status `done`, and a Machine done at start is `done` before its init relay. And
130
+ xstate never stops an errored callback, so its own later sends still reach its parent with no cause. Receipts, poll
131
+ answers, Allowed picks and refusals do not change: the model reads what it read before.
132
+
133
+ - **Same Turn or not is the target's position, and nothing else.** A pick whose target is inside the state that invokes
134
+ the Agent — a mode, a child that runs something, a Gate, another Agent — keeps the Turn; a pick that leaves that state
135
+ ends it, and the next prompt carries the result. A pick with no target keeps the Turn open. So a Turn may hold many
136
+ picks, and the one that leaves the state ends it. What the child does changes nothing: there is no second mechanism.
137
+ Child or sibling says only this — same Turn or not — and never who may send what: that is the audience rule's, by the
138
+ party each state invokes (ADR-0015).
139
+ - **A pick that moves the workflow inside the Agent's state is answered at Rest.** Rest: no state below the Agent's
140
+ state is running something — an invoked child that is still active (an Effect, a Gate, another Agent, a child Machine,
141
+ a promise) — and no pending timer will move the workflow to another state inside the Agent's state. A timer counts the
142
+ same whatever wrote it (`after`, a named delay, a delayed `raise`, a delayed `sendTo` back to the Machine): it holds
143
+ Rest when an active state at or below the Agent's state handles its event with a target inside it; a timer that leaves
144
+ the Agent's state (a Turn timeout) holds nothing. A settled child with no `onDone` is Rest, and a state named `rest`
145
+ means nothing to jr2, so a forgotten route cannot hang a Turn. Rest is read from the Machine, never kept: the snapshot
146
+ and the timer table (ADR-0007), after the mailbox drains (`rest.ts`), so a restored run gives the same answer as
147
+ before the restart. It is computed over the SET of active states below the Agent's state; nothing assumes the set has
148
+ one member. Known limit: a delayed `sendTo` to a child whose reply then moves the Machine is not counted.
149
+ - **While a pick is outstanding — delivered and not yet answered — every other pick is refused**, `finish` included, and
150
+ nothing is delivered; so is every pick while the Agent's state is busy with work it started in. xstate itself would
151
+ take the pick, and one the Agent's state handles would exit the child state and stop the work running there, so the
152
+ refusal is jr2's. The Orchestrator decides it from the Machine (Rest) and the run's saved holds, so it holds after a
153
+ restart, and answers `409 { refused: "outstanding", outstanding?, allowed: [] }`. The Allowed picks are empty while
154
+ the Agent's state is not at Rest (ADR-0029): the Rest rule and the payload-blind selection give one list. The engine
155
+ runs a model answer's calls one at a time, in the model's order
156
+ ([ADR-0027](0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md)). Only the FIRST pick of a model
157
+ answer runs. A later pick of the same model answer does not run, whatever its name. The model reads "This pick did not
158
+ run: you sent it before you read the answer to `<first pick>`." The model wrote that pick before it read the first
159
+ pick's answer, so it is stale by construction. The first pick is the first pick of the model answer that the engine
160
+ asks `beforeToolCall` about. The engine reports each model answer before it asks about its calls, and it asks them in
161
+ the model's order (ADR-0027). It asks only about a call of a tool the run has, with valid arguments, so a call of an
162
+ unknown tool, or with invalid arguments, is never the first pick. So the Harness keeps no claim and has no race. The
163
+ blocked pick is still a step for the Runaway watch
164
+ ([ADR-0035](0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md)). A call after a pick whose answer ended the
165
+ Turn (`turnComplete`, or a 404) does not run either, a Working-tool call or a pick. The Harness blocks it, and the
166
+ model reads "This call did not run: a pick before it in your answer ended your turn." Under `continue: true`, the next
167
+ state registers the same address, so such a pick would reach the NEXT Turn. The run then ends at the step boundary
168
+ (`endsRun`). The Harness sends no further model request for a Turn that is over, whether or not the Orchestrator's
169
+ abort has landed. So the model reads that text, and the Turn-ending receipt, only in the NEXT Turn of a continued
170
+ conversation. They give no order to stop, because that order is false in a later Turn. The Harness does not settle
171
+ that Submission: it waits for the sweep (above). An `ended` Settlement carries no error object and no message. Nothing
172
+ went wrong, and the outcome itself names the cause. An `aborted` one that the sweep settles has no message. One whose
173
+ wait for the sweep ran out says so ("… no sweep came from the Orchestrator …").
174
+ - **The answer, in order:** the work settles, the Machine's route runs (guards, assign), then the Agent is answered with
175
+ the Allowed picks read AFTER the route. The answer is the last reply since the pick from below the Agent's state — a
176
+ Gate's event and payload, another Agent's pick, a child's output — and the Harness writes the words. With an `answer`
177
+ action the author shapes it instead (ADR-0067): the action reads the CHAIN, every reply since the pick, oldest first,
178
+ and its value is the answer; the last call wins. One written below an Agent's state — on a state's entry or exit, or
179
+ on a transition, at or below that Agent's state — shapes only that Agent's pick, whatever event ran it. On a
180
+ transition below no Agent's state (the root, or a parent of two Agents' regions) it shapes the pick of the Agent whose
181
+ event took the transition: the sender of a pick, or the Agent whose pick started the child whose done or error event
182
+ it is. The run records which Agent started each live child, and the invoke of the Agent's state its pick was made at —
183
+ those a pick's own send started; those started while the Machine took an event that is already that Agent's (a done or
184
+ error event of a child it started, or its own pick); and those a timer or another event that names no Agent started on
185
+ a state below the Agent's state while its pick is outstanding, which the Rest rule makes that pick's work (below two
186
+ Agents' states, one inside the other, the nearer one's) — so the work a pick began stays its Agent's, whether the pick
187
+ is still outstanding, was answered while the child ran, or was answered on its own `POST`, and the record holds
188
+ through a restore. The record keys each child by the chain of invoke ids from the root down to it, since an invoke id
189
+ is local to its Machine: two Machines of one tree, and the workers of a pool, which run one body, may each have a
190
+ child of one id, and one never reads or changes the other's entry. A child started on an event that names no Agent
191
+ anywhere else is no Agent's. The other Agent's pick keeps its default answer, and when the Agent the event names has
192
+ no pick being answered, it sets nothing. One Agent slot can be invoked by two states that are not active together (two
193
+ states one after the other). Then it shapes the pick at the same invoke as the pick that sent the event or started the
194
+ child, and nothing otherwise: the done event of a child that the earlier state's pick started, after that pick's Turn
195
+ ended (its child runs on), shapes no pick, and the later Turn's pick keeps its default answer. When the event names no
196
+ Agent (a timer, a Gate's answer, an outside send, the done event of a child no Agent's pick started), it shapes the
197
+ pick in its own send; outside one, it shapes the one such pick outstanding only while one Agent's state is active.
198
+ Outside a pick's own send, while two Agents' states are active, it sets nothing, whatever is outstanding: the event
199
+ cannot say whose pick it is. On an entry or an exit below no Agent's state it sets nothing, whatever is outstanding,
200
+ and the load walk warns, naming the Agent states it could be moved under: an entry or exit there is no transition an
201
+ Agent's event takes. One action object written in two places runs from each by that place's rule alone, never by their
202
+ sum: written below two Agents' states, from A's state it shapes only A's pick and from B's only B's; written below A's
203
+ state and on a transition below none, from A's state it shapes only A's pick and from the transition the pick of the
204
+ Agent the event names; written on an entry or exit below no Agent's state as well, it sets nothing as that entry or
205
+ exit. An `answer` that an `enqueueActions` enqueues, at any depth, runs from the place the enqueuing action is
206
+ written, by that place's rule: enqueued on an entry or exit below no Agent's state it sets nothing, and enqueued below
207
+ an Agent's state it shapes only that Agent's pick. The load walk cannot see inside an `enqueueActions`, so it does not
208
+ warn on one enqueued on an entry or exit below no Agent's state. An `answer` that runs while no pick of that Agent is
209
+ being answered (a state's first entry, a later Turn's re-entry, another Agent's region of a parallel state) sets
210
+ nothing. A reply comes from a child invoked on an active state below the Agent's state, or from a party whose Gate
211
+ invoke id or Agent slot is invoked there (its sender, ADR-0015). The events the Machine sent itself (a `raise`, a
212
+ `sendTo(self)`, a timer) are no reply, and neither is an event from outside that set (a Gate in a sibling region, a
213
+ root Gate, an outside send), nor a child's error: every outcome of work is its output, and an error is a jr2 or config
214
+ defect, so by default its message never reaches the model. The operator reads it: while a pick is outstanding, an
215
+ error from a child below the Agent's state goes on the run's feed (`child-error`: the Agent, its pick, the child and
216
+ the message; Instance-token band, as `fault` is), and `jr2 run` and `jr2 logs` print it. An author who wants the Agent
217
+ told passes it explicitly: `answer(...)` on the `onError` route (ADR-0067), or an `onError` route to a state whose
218
+ prompt carries it. With none, the answer carries no reply, and its words say only that ("No reply came back for it"):
219
+ never that nothing ran, because a timer or an outside event may have stopped work that did run. The pick's `POST`
220
+ answers at once with `outstanding` and a delivery id, and the Harness long-polls the answer inside the one tool call
221
+ (ADR-0002, ADR-0013). A child that settles inside the pick's own send — a decision Machine that is final as it starts
222
+ — replies before the `POST` answers: when that reply leaves the workflow at Rest, or out of the Agent's state, the
223
+ `POST` itself is the answer, with `answered` and nothing to poll. So is a guard straight back: a pick that enters a
224
+ state and leaves it again inside its own send (an `always` back), with no reply, leaves the state value as it was, and
225
+ is answered with `straightBack` — the one answer that says "it moved the workflow, and nothing ran". An outstanding
226
+ pick's answer never carries it. A pick that changes the state value moved to a mode, and gets the plain receipt with
227
+ its Allowed picks; a pick with no target went nowhere, and gets the same, as does a pick whose target is the state it
228
+ was taken in (it enters nothing it leaves). Every receipt that leaves the Turn open (the plain receipt, a guard
229
+ straight back, and an answered pick back at Rest) ends its state part with the same words, "Your turn is not over.":
230
+ the plain receipt once said "the workflow is still in the state that asked for this turn, so it is not over yet", and
231
+ real Haiku 5.5 runs stopped after it, because "it" can be the workflow or the Turn. The refusals and the notices of a
232
+ cut or unsent pick keep their own words. A route that leaves the Agent's state ends the Turn like any other: the poll
233
+ reads `turnComplete`, the next state's prompt is the author's, and the answer is a `pick-answer` notice to the Agent
234
+ that picked: the next Turn of the conversation the picking Turn continued (`continue: true`, ADR-0062). A FRESH
235
+ picking Turn's conversation is never heard again, and a later continued Turn of the same slot never made the pick, so
236
+ its answer goes nowhere: the ledger keeps no notice for it. The route of a reply includes the events the Machine sends
237
+ itself AT ONCE while it runs (a `raise`, or a `sendTo(self)` with no delay, in `onDone` or in the next state's entry):
238
+ a reply whose route leaves the Agent's state that way is the answer, never stopped. Something other than the route can
239
+ leave the Agent's state while the pick's work still runs below it — a timer the Machine set (a Turn timeout `after`, a
240
+ named delay, a delayed `raise` or `sendTo(self)`), an event from outside the states below, a Gate's event, the Agent's
241
+ own end. The exit stops that work (an invoke is stopped when its state exits), and the answer says so (`stopped`): the
242
+ poll reads "The workflow left the state that asked for this turn while it ran your pick, and that stopped the pick's
243
+ work before it finished: your turn is over.", and the next continued Turn's notice reads "Your pick `X` did not
244
+ finish: the workflow left the state that asked for your last turn while it ran, and that stopped it." Both keep the
245
+ last reply an earlier link gave, if any, and state it: an earlier link's reply is never dropped because a later link
246
+ was stopped. The run host tells a fired timer from an event sent at once by the scheduler's own table: it watches what
247
+ the actor system arms, cancels and fires (`timers.ts`, the same xstate internals as the timer table, ADR-0007). Back
248
+ at Rest INSIDE the Agent's state — a per-step timeout `after` on the child state that exits the work — the answer is
249
+ the last reply since the pick, with the Allowed picks; with no reply since the pick, the answer carries none. A
250
+ restored hold is the Turn of the first registration of its address in the restored tree: a restore that ends that Turn
251
+ and starts another at the same address before anything settles answers the pick with `turnComplete`. There is no
252
+ default time bound on a held Turn; the Console shows each outstanding pick "held since".
253
+ - **The one exception is the Orchestrator ending it for its own reasons**, which is ADR-0007's requirement and exactly
254
+ one place: `RunHost.stop()`. Process shutdown never stops actors at all (`instance.ts` calls `host.close()`, which
255
+ ends observation feeds and nothing else), and restore is a fresh process. So "the host did this" is a flag it sets,
256
+ not a fact to be inferred.
257
+ - **The rule is invocation-end, not "the Agent's own pick settled the state".** The narrower rule was tempting and is
258
+ wrong twice over: it rests on `sendBack` being synchronous — true in xstate 5.32.2, verified, but an internal that a
259
+ version bump could turn off _silently_ — and it misses every other ending while a submission lives: an `after:`
260
+ timeout on an agent state, an ancestor or sibling transition, a Pool cancelling a child. Several of those are
261
+ self-limiting because the Sandbox dies with them; the dangerous ones are the endings where the Sandbox deliberately
262
+ survives, and `humanReview` is precisely that. One rule, no library internal beneath the guarantee.
263
+ - **Abort interrupts a submission mid-tool-loop.** Measured against the live pod, aborting the runaway reviewer:
264
+
265
+ | window | reviewer log lines | `review_verdict` calls |
266
+ | ----------------- | ------------------ | ---------------------- |
267
+ | 40 s before abort | +20 | **+20** |
268
+ | 60 s after | +0 | **0** |
269
+ | +45 s further | +0 | **0** |
270
+
271
+ `{"aborted": true}`, settling to `{"outcome":"aborted","error":{"type":"submission_aborted"}}` beside the coder's
272
+ `{"outcome":"completed"}`. The run was untouched: still active, still parked, no transition and no `agent.fault`. That
273
+ sweep said no cause, because the field did not exist yet. The reviewer's own pick had ended its Turn. So today the
274
+ sweep says `cause: "pick"`, and the Submission settles `ended`.
275
+
276
+ - **jr2 never observes that settlement, and the ADR does not pretend otherwise.** By construction the actor is already
277
+ stopped when the abort fires — being stopped is the trigger — so `settle`'s rejection was swallowed by
278
+ `if (stopped) return` before the remote outcome existed. The wire's distinct `ended` and `aborted` outcomes are worth
279
+ having for **observability**: `history` reads `ended` or `aborted`, not `failed`. They are not what suppresses a
280
+ spurious fault. Nothing suppresses it, because nothing is listening. The wire client's `wait` resolves on `completed`
281
+ alone. So a stopped actor drops an `ended` as it drops every rejection. No path reads it as a run that returned with
282
+ the Turn open.
283
+ - **`AgentRunPort` gains `abort(agentName, instanceId)`**, so the port stays the injectable seam and a unit test can
284
+ assert the abort without a live Harness — the same shape as `admit`/`settle`.
285
+ - **An abort is ordered before the next `send` on the same instance id.** The run binding keeps `iid → pending abort`,
286
+ and `agentRun` awaits it before `admit`. This is `continue: true`-scoped by construction: a fresh session mints a
287
+ random suffix and cannot collide. Without it the feature loses turns **silently** — see below.
288
+ - **The receipt becomes self-describing**: `POST /agents/:iid/events` answers
289
+ `{ delivered: true, event: "review_verdict", turnComplete: true, deliveryId }`. `turnComplete` is read off the same
290
+ table the guarantee uses — whether the registration just delivered to is still live — so it reports what happened
291
+ rather than what was hoped; for an outstanding pick it is read at Rest, on the answer. `deliveryId` addresses that
292
+ answer ([ADR-0013](0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md)). **This is the hint, not
293
+ the guarantee**, and it is named for the Agent's frame rather than the wire's: the Agent has no submissions, it has a
294
+ Turn (GLOSSARY.md).
295
+ - **The Harness renders the receipt as prose**, because a model reads the tool result's text: the pick was delivered,
296
+ the workflow consumed it, the turn is over.
297
+ - **The Agent instructions gain the stop half.** "You MUST finish by calling `review_verdict`" is half a contract — it
298
+ says how to finish and never that finishing is finished. The Agent definitions a Machine carries (an Agent is a slot
299
+ on the Machine that carries it — ADR-0049) say to call it **once**, then stop. The receipt of a pick that ends the
300
+ Turn says that the turn is over and gives no order: the run ends at that step boundary, so the model reads the receipt
301
+ only in a later Turn of a continued conversation (ADR-0027).
302
+
303
+ ## The Harness queues, and the queue is load-bearing here
304
+
305
+ Invoking a `continue` iid that is already live does **not** fail loudly — the Harness **queues**: prompts for one
306
+ instance enter one per-instance queue in admission order, and a Submission is promoted only when it is the first
307
+ unsettled one for its conversation ([ADR-0027](0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md)
308
+ asserts these semantics as jr2's own; they were first discovered, mid-incident, as the behavior of the since-retired
309
+ harness runtime — the original ADR-0016 assumed the opposite, a loud per-iid fence). That cuts both ways, and both
310
+ matter:
311
+
312
+ - **`continue: true` was unusable before this ADR, not merely fragile.** A runaway submission does not fail the next
313
+ state's `send` — it makes it queue behind work that may never finish, and the state hangs with no error.
314
+ - **Ordering is mandatory, not hygiene.** Abort sweeps the running Submission and everything queued behind it
315
+ (ADR-0027). An abort that loses the race to the next `send` therefore kills the new turn **before it runs at all** —
316
+ no error, no fault, the Agent simply never speaks. Silent turn loss is the worst failure mode available, which is why
317
+ the ordering ships with the decision rather than after it.
318
+
319
+ ## Considered options
320
+
321
+ - **The receipt and the instructions alone.** The cheap half, and it is in the decision — as a hint. Rejected as the
322
+ guarantee by the coin-flip evidence: the model already had instructions and answered failure by degrading, not
323
+ stopping.
324
+ - **Let the 404 do the work** (today). Rejected: it is the mechanism that produced 41 calls. A transport error reads as
325
+ "try again".
326
+ - **Keep a tombstone so a call after the turn answers "already delivered" instead of 404-ing.** Genuinely tempting while
327
+ the 404 was the only signal. Rejected once abort exists: the submission is dead within a round trip, so the tombstone
328
+ buys a nicer message for a window in which almost no call can occur, in exchange for unbounded server state.
329
+ - **`defineEvent({ endsTurn: true })` — let the author declare it.** Explicit, no inference. Rejected: it pushes
330
+ mechanism back onto the author, the exact direction [ADR-0015](0015-authoring-surface-absorbs-the-mechanism.md) spent
331
+ a whole decision reversing. Invoke semantics already answer the question.
332
+ - **Abort on actor stop, unconditionally.** Rejected: it breaks ADR-0007's restore, which is the reason `abandon()`
333
+ abandons locally in the first place.
334
+ - **Abort from the host, in `sendToAgent`.** Rejected three ways: it puts a data-plane client in the delivery path
335
+ (ADR-0013 answers deliveries from the registration table alone), it makes a delivery await a remote call before
336
+ answering, and it only ever covers endings caused by deliveries — the narrow rule again, wearing a different hat.
337
+ - **Take the Menu's tools away when the surface goes.** Rejected: nothing pushes to the Harness by design (ADR-0013 — it
338
+ asks per turn, and the answer is the turn), and a tool disappearing does not stop a model already generating.
339
+ - **Make the event `deferred` and hold the call open** as the way to END a Turn. Rejected: it solves a different problem
340
+ — giving the Agent an _answer_ — where this one is about giving it an _end_. The answer is now given, by Rest and the
341
+ poll above, to every pick that runs work inside the Agent's state, with no per-event declaration.
342
+ - **Answer an outstanding pick from the POST itself, held open.** Rejected: undici cuts it at 300 s and its error then
343
+ says the delivered pick never arrived; an Orchestrator restart mid-request reads as unreachable (ADR-0002).
344
+ - **A per-pick footprint as the Rest rule** (Rest when what THIS pick started has settled), or explicit `rest`-tagged
345
+ states. Rejected for the one-path shape jr2 builds: the footprint needs a per-pick record that a restore must carry,
346
+ and a tag makes a forgotten mark hang the Turn. A footprint is the natural rule for the shape jr2 reserves — picks the
347
+ Agent does not wait for, in parallel regions (ADR-0015) — if that is ever built.
348
+ - **A Harness claim beside the Orchestrator's refusal.** The first pick of a model answer held it. The Harness refused
349
+ every other pick of that model answer while the first was out. It was needed while pi started the calls of one model
350
+ answer at the same time. Two sockets did not keep the model's order (measured: `finish` arrived first in 1 of 50).
351
+ Rejected: it is state that a race decides. Since the engine runs the calls one at a time (ADR-0027), the Harness finds
352
+ the first pick from the model answer instead.
353
+ - **Deliver a second pick of one model answer after the first is answered, and let the Orchestrator judge it** by the
354
+ Allowed picks after the first pick's route. Rejected: the model wrote the second pick before it read the first pick's
355
+ answer, so the pick is stale by construction. An Allowed pick can still be the wrong one, for example `finish` after a
356
+ `run_tests` whose answer is a failure.
357
+ - **End a Turn that is over with pi's `terminate` flag on the pick's result**, not with `endsRun`. Rejected: pi stops
358
+ only when every result of the batch sets it, so a model answer `[bash, pick]` would go on to another request.
359
+ - **`aborted` for every end of a Turn**, the Turn its pick ended included. Rejected: one word had three meanings. The
360
+ Turn's own pick ended it (a success), something else stopped it, or it was over before it started. A reader takes
361
+ `aborted` as "something stopped it".
362
+ - **`completed` for a Turn its pick ended.** Rejected: `completed` would then hold two opposite cases. One is a Turn
363
+ that reached the end its workflow asked for. The other is a model that stopped early in a Turn that is still open,
364
+ which the Agent's Actor nudges. `ended` is GLOSSARY.md's own verb: a Turn "ends when that state stops waiting", by
365
+ "the pick that ends it".
366
+ - **`answered` as the name.** Rejected: "answer" already means a pick's answer, a model answer, and the `answered`
367
+ `@model` claim.
368
+ - **Let the outcome follow whichever end lands first:** `ended` when the Harness ends the run before the sweep lands,
369
+ and `aborted` when the sweep is first. Rejected: the outcome would follow a race. The `@kind` tier counts exact
370
+ `ended` settlements (see Consequences).
371
+ - **Let each end judge, and make the two judgments agree.** The Harness would read the pick's answer (`turnComplete`
372
+ with no `stopped`), and the Orchestrator the same facts. Rejected after it was built: the two read different signals.
373
+ "Was the pick's work stopped" is not "what left the state". Three verifications each found ends where they disagreed.
374
+ One was a timer while the pick waited on a timer below. Others were a fault caught above, a CANCEL during an
375
+ outstanding pick, and an exit in the same tick as a Rest. One judge cannot disagree with itself.
376
+ - **Read the Rest before the Machine takes an event, to answer a pick early.** Rejected: `rest.ts` forbids it. It also
377
+ gave the model wrong answers: another Agent's pick answered with no reply, and a pick answered at a Rest that a queued
378
+ event ended.
379
+ - **Keep the cause on the event object.** Rejected: xstate relays one object each time a config action with an object
380
+ literal runs. So an object that the pick's route sent once kept that cause when a timer sent it later.
381
+
382
+ ## Consequences
383
+
384
+ - **The primary win is correctness**: a Workspace stops carrying Agents the Machine does not know are running. Tokens
385
+ and wall-clock are the secondary benefit, and they are large — the tail of an ended turn is currently unbounded.
386
+ - **`continue: true` becomes usable for the first time.** Before this ADR no workflow exercised continuation, which is
387
+ why the queue semantics went unnoticed; `task` (ADR-0054) exercises it now.
388
+ - **A failed abort is not reportable.** The actor is stopped, so there is no `agent.fault` to raise, and the run's
389
+ telemetry channel carries only `RetryTelemetry`. The call is fire-and-forget. An Agent whose sweep is lost still 404s
390
+ on every tool call and ends its run. It settles `aborted` when its wait for the sweep runs out. Widening telemetry is
391
+ a separate decision.
392
+ - **`turnComplete` fails conservatively.** It is read after `deliver` returns and depends on `sendBack` being
393
+ synchronous. Should that stop holding, it reads `false` — today's behavior, never a false claim. The guarantee no
394
+ longer depends on it at all. `turnEndedByPick` rests on the same fact and fails the same way. The pick's send would
395
+ not stop the invocation, and the sweep would say no cause. The Turn would then settle `aborted`, never a false
396
+ `ended`.
397
+ - **The limits of the one judge.** Each can give `aborted` (or, for a restart, `failed`) to a Turn that its own pick
398
+ ended. None gives `ended` to a Turn that something else ended:
399
+ - **An Orchestrator that is down when the Turn ends** sends no sweep. So the Submission settles `aborted` when the
400
+ Harness's wait runs out. So does an Orchestrator whose sweep fails on the way: the sweep is fire-and-forget (above).
401
+ - **Version skew.** An Orchestrator from before `cause` sends no body, so its Turns settle `aborted`, every time. A
402
+ Harness from before `cause` ignores the body, and settles every sweep `aborted`.
403
+ - **A restored run.** A timer's cause is kept in memory, on its `setTimeout` of the run's clock. A timer that a
404
+ restore arms again has no cause, so its exit sends a sweep with no cause.
405
+ - **A Machine above the Agent's Machine faults, and a Machine above it catches the fault.** xstate does not stop the
406
+ errored Machine's children, so the Agent's Machine runs on. The run took the Agent's state away when it caught the
407
+ fault, so that is the exit, and it has no cause, also when the pick's route sent the event that caused the fault. A
408
+ later route of the Agent's Machine that stops the Agent sends a sweep with no cause, and the Submission settles
409
+ `aborted`.
410
+ - **The Agent's own Machine faults, and a Machine above it catches the fault.** xstate does not stop that Machine's
411
+ children, so the Agent's actor is never stopped. Its surface stays live, and no sweep is sent. A pick of that Agent
412
+ finds no end of the Turn, so its Harness does not wait for a sweep, and nothing settles the Submission. This orphan
413
+ is older than this rule, and it is a separate defect.
414
+ - **An exit while the next admission is in flight.** A nudge, a wake or a re-admission is ledgered only when the
415
+ Harness answers it. A Turn that its own pick ends in that window names the Submission before it. The Harness no
416
+ longer runs that one, so the running Submission settles `aborted`. This is conservative, and no wait is added for
417
+ it.
418
+ - **A Harness that restarts while it waits for the sweep** settles the Submission `aborted`, with the message that no
419
+ sweep came. It recorded that the run ended because its Turn was over. A Harness that restarts while the run still
420
+ runs settles it `failed`, as for every Submission that a restart cut (ADR-0031).
421
+ - **The nudge (ADR-0016) answers the opposite failure** — a model that stops while its Turn is still open with picks
422
+ left. It reads the Allowed picks after the Submission settles — or, if none are left then, the first time they open
423
+ while the state is active (the wake) — so a pick that kept the Turn open can be followed by a nudge, and a pick that
424
+ ended it never is: the abort and the nudge cannot fire against each other.
425
+ - **A Machine author can hold a Turn for long by accident.** A Gate below the Agent's state whose answer has no target
426
+ stays open after it, and a long-lived invoke below it (not a promise, an Effect, a Gate, an Agent or a child Machine)
427
+ never settles: either keeps the Agent's state from Rest, and every pick of the Agent waits. The load walk warns on
428
+ both (ADR-0015's walk); neither is refused, because either can be meant.
429
+ - **Rest reads xstate internals**: the timer table (`system.getSnapshot()._scheduledEvents`, as ADR-0007's re-arm does)
430
+ and the seat lookup's active nodes. The raw-xstate tests are the canary for a bump.
431
+ - **Non-determinism dictates how this is tested.** "Did it stop calling?" proves nothing when a well-behaved agent stops
432
+ on its own. The `@kind` tier asserts the **settlement outcome**, in two scenarios. In the `handoff` scenario, the
433
+ Agent's pick ends its Turn, and that Turn settles `ended`, whichever end lands first. The next `continue` Turn, which
434
+ `shipping` asked for, is not swept: the count of `ended` settlements stays 1 while `shipping` is active. In the
435
+ `interrupted` scenario, a Gate's answer, not a pick, ends the Turn while the scripted model holds its request. That
436
+ Turn settles `aborted` through the sweep, with no message, and the abort cuts the model's request.
@@ -0,0 +1,65 @@
1
+ # CANCEL ends a run; stopping one parks it
2
+
3
+ `jr2 send <run> --event CANCEL` advertises "abandon a live run" (`cli.ts:29`), and
4
+ [ADR-0009](0009-cli-and-instance-interface.md) calls it the run-level infra interrupt. It was neither. The route called
5
+ `RunHost.stop()`, which stops the actor and **deliberately leaves the stored status `"live"`** — the tracked-run guard
6
+ drops the scheduled save — so the next `restore()` picks the run back up where it left off. A human abandoned a run and
7
+ it came back on the next orchestrator restart.
8
+
9
+ That was already wrong; [ADR-0024](0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md) made it load-bearing.
10
+ Its one exception — the ending that must NOT end the Agent's turn, because ADR-0007's restore re-attaches to those
11
+ submissions — is keyed on `RunHost.stop()`. And in a deployed Orchestrator that method has **exactly one caller: the
12
+ CANCEL route** (process shutdown stops no actors; the other callers are tests simulating a restart). So the exception
13
+ carved out for "the Orchestrator ending a run for its own reasons" was being spent, in production, on a human saying
14
+ "stop". Cancel a run and its Agents kept generating, in a Workspace nothing was watching.
15
+
16
+ ## Decision
17
+
18
+ - **Two verbs, because there are two meanings.**
19
+ - `RunHost.stop(runId)` — **park**: stop hosting the run here, keep it `"live"` and restorable, leave the submissions
20
+ alive. ADR-0024's exception, unchanged. The teardown primitive, and the seam an orchestrator restart is simulated
21
+ through.
22
+ - `RunHost.cancel(runId)` — **end**: stop the actor with no exception flag, so every live `agentRun` invocation ends
23
+ its Agent's turn (ADR-0024), then persist the run **terminal**. `POST /runs/:id/events {CANCEL}` calls this one.
24
+ - **Ending the turns and refusing to restore are ONE decision, not two.** A cancelled run that came back would re-attach
25
+ from the admission ledger to submissions that settled `aborted`; `settle` would reject `submission_aborted` and fault
26
+ a run whose Agents were stopped on purpose. Abort and restorability are incompatible, which is exactly why ADR-0024
27
+ made its exception the whole method rather than a per-call flag.
28
+ - **The terminal status is `cancelled`** — a run-lifecycle word. xstate reports a stopped actor as `"stopped"`, which is
29
+ mechanism, not an outcome. So the **store row is the authority on the run's lifecycle and the snapshot on the
30
+ Machine's**: `read()` prefers the row unless it still says `"live"`. A cancelled run stays readable — `jr2 status`
31
+ reports `cancelled` plus the state it was in when it was cancelled — and `restore()` skips it, because it skips
32
+ everything that is not `"live"`.
33
+ - **`stop()` stays public with no production caller, and is deliberately not on the wire.** It is correct, it is what
34
+ the durability suite drives, and it is what a future graceful shutdown should call. What nobody asked for is a
35
+ user-facing "park this run" verb.
36
+ - **A cancelled run's Workspaces are destroyed, unless the cancel says `keep`.** The run is over, so nothing can use
37
+ them, and each holds its whole Size (ADR-0060) — a placed pod left to the idle GC is capacity for nobody. The
38
+ wrapper's `teardown` is a STATE, entered when the body reaches final, and an actor stop skips it; so the host destroys
39
+ every Sandbox the run owns after the stop, the same idempotent delete `teardown` runs.
40
+ `POST /runs/:id/events { type: "CANCEL", keep: true }` — `jr2 send <run> --event CANCEL --keep` — leaves them for
41
+ inspection instead: with no Lease they age out at the operator's idle timeout, and `jr2 ssh` reaches them until then.
42
+ Whether to keep is a fact of the moment of the cancel, never a config key. A **faulted** run keeps its Workspaces as
43
+ before ([ADR-0012](0012-workspace-wrapper-machine.md)'s destroy-less terminal, ADR-0021): a fault is the case nobody
44
+ chose, and the pod is the evidence. A **failed** run deletes them, as a `done` run does: a Failure is an end that the
45
+ Machine chose ([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)).
46
+
47
+ ## Considered options
48
+
49
+ - **Flip ADR-0024's flag on the CANCEL route and change nothing else.** The obvious one-liner, and wrong: the run stays
50
+ restorable, so the abort's only lasting effect is an `agent.fault` after the next restart.
51
+ - **Redefine CANCEL as "stop hosting" and document it honestly.** Rejected: it contradicts the CLI's own help text and
52
+ ADR-0009/0011's "interrupt", and a park verb is not what anyone reaches for `--event CANCEL` to do.
53
+ - **Reuse `markLost`.** Rejected: `lost` means the live world went away underneath a run we wanted to keep (restore's
54
+ reconcile probe). A cancelled run is not lost, and conflating them makes both statuses unreadable — the reason
55
+ `markLost` also clears the snapshot, which a cancelled run must keep.
56
+ - **Have `cancel` destroy the Workspace too.** Rejected above: silent cleanup destroys the evidence.
57
+
58
+ ## Consequences
59
+
60
+ - **`jr2 send --event CANCEL` now does what it says**, including on the Agents: their turns end with the run.
61
+ - **A cancelled run is readable but not resumable.** Nothing in the kit resumes one today anyway — `stop()` was the only
62
+ path back, and nothing called it.
63
+ - **The mechanics tier owns this contract** (ADR-0010): a real CLI CANCEL against a real Orchestrator, asserting the run
64
+ settles `cancelled` and the stub Harness reports the turn settled `aborted`. The `@kind` tier is unchanged — the
65
+ cluster adds nothing to a claim about run lifecycle.
@@ -0,0 +1,62 @@
1
+ # A Turn that is over is never prompted, and only a Turn that is over answers 404
2
+
3
+ The Harness reads its surface at the start of each Submission: `GET /agents/<iid>/surface`, through the pod's Custodian
4
+ ([ADR-0013](0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md)). An iid with no registration
5
+ answers **HTTP 404**. The Orchestrator registers the surface before every admission — the first, a reroll's, and a
6
+ nudge's, which reuses the live one — and removes it only when the invocation ends, which sends
7
+ [ADR-0024](0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)'s abort. So a 404 at the start of a Submission
8
+ is one race: the state exited (an `after:` timeout, an ancestor or sibling transition, the Agent's own pick) before the
9
+ read landed, and the abort is on its way.
10
+
11
+ This ADR's first form read that 404 as an empty Menu and prompted the model anyway. That spent a model call on a Turn
12
+ nobody waited on, wrote a dead Turn's prompt into a continued conversation's history, and — once the Menu became fixed
13
+ for a conversation ([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)) —
14
+ presented a tools block that broke its cache.
15
+
16
+ One case broke the premise. A stopping Orchestrator stops its runs **before** it closes its listener (the feeds need
17
+ that order), and `hostStopping` removes their registrations with no abort, because ADR-0007's restore re-attaches. In
18
+ that window a surface read answered 404 for a Turn that was not over, and a pick was told "this turn is over". Both were
19
+ false: the Orchestrator was going away, not the Turn.
20
+
21
+ ## Decision
22
+
23
+ - **A 404 at Submission start settles the Submission `aborted`, and the model is never asked.** Nothing is written to
24
+ the conversation. It is not `failed`: nothing is wrong, and a signal that cries wolf cannot also be the alarm. It is
25
+ not `ended` either, and it does not wait for the sweep. This Submission ran nothing, so the Orchestrator has no end of
26
+ it to judge ([ADR-0024](0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)). ADR-0024 already says jr2
27
+ never observes the settlement of a Turn it aborted, so the Orchestrator side does not change.
28
+ - **Only a Turn that is over answers 404.** While its host is stopping, and while a boot with no current Assembly still
29
+ waits for the publish that restores its runs
30
+ ([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)), the Orchestrator answers
31
+ `/agents/*` with **503**, not 404, for a surface it does not host. The Harness treats a 503 on the surface read as
32
+ unanswered and re-asks on ADR-0042's ladder, which outlasts a rollout; a boot with a current Assembly restores its
33
+ runs before it listens, and a boot without one restores them at the first publish, so the re-ask finds the live
34
+ surface either way. A pick that gets 503 fails with "try it again", as a transport failure does — `deliver` still
35
+ never retries on its own (ADR-0042).
36
+ - **Acting against a Turn that is over still fails loudly.** A pick delivered after the state moved on gets 404 on
37
+ `POST /agents/:iid/events`, and the Harness throws it to the model as "this turn is over". The run then ends at the
38
+ step boundary. The Submission waits for the Orchestrator's sweep, which says how the Turn ended (ADR-0024). The
39
+ Harness does not read a 404 as a cause.
40
+ - **`GET /agents/:iid/surface` on the Orchestrator stays a plain REST resource,** 404 for no registration; the mechanics
41
+ tier asserts against it.
42
+
43
+ ## Considered options
44
+
45
+ - **Read the 404 as an empty Menu and prompt anyway** (this ADR's first form). Rejected: a model call and a history
46
+ entry for a Turn that is over, and a tools block that differs from the conversation's Menu.
47
+ - **Keep the conversation's last Menu and state "Allowed picks: none".** Rejected: it presents a dead Turn more
48
+ carefully instead of not presenting it.
49
+ - **Settle the Submission `failed`.** Rejected: the 404 is routine; a failure there hides the one that means something.
50
+ - **Hold the registration open one extra round trip.** Rejected: it makes Turn lifetime depend on connection timing, and
51
+ any window is a guess.
52
+ - **Close the listener before stopping the runs.** Rejected: it would reorder shutdown against the feeds, and a Harness
53
+ would see transport failures it already retries — the 503 says the same thing without moving shutdown.
54
+
55
+ ## Consequences
56
+
57
+ - **A failed surface read means something.** An unanswered read past ADR-0042's window, a 403, or a surface offering an
58
+ event the Harness refuses to present (a `poll` def, ADR-0013) settles the Submission `failed` before the model is
59
+ asked.
60
+ - **An Orchestrator rollout no longer tells a live Turn it is over.** The surface read waits it out; a pick in the
61
+ window is retried by the model on a clear transport-class error, and the no-signal nudge (ADR-0016) still catches a
62
+ Turn that ended without one.