@jr2/orchestrator 0.5.1 → 0.7.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 (350) hide show
  1. package/console/components/app.ts +55 -15
  2. package/console/components/bottom-drawer.ts +259 -0
  3. package/console/components/drawer.ts +14 -6
  4. package/console/components/fleet.ts +36 -13
  5. package/console/components/machine-pane.ts +61 -5
  6. package/console/components/nav.ts +54 -8
  7. package/console/conversation.ts +161 -0
  8. package/console/login.ts +123 -0
  9. package/console/main.ts +600 -60
  10. package/console/store.ts +611 -64
  11. package/console/style.css +231 -0
  12. package/docs/agents-block.md +2 -2
  13. package/docs/concepts/agents-and-turns.md +142 -29
  14. package/docs/concepts/architecture.md +60 -34
  15. package/docs/concepts/callers-and-hooks.md +205 -0
  16. package/docs/concepts/charts.md +13 -8
  17. package/docs/concepts/composition.md +10 -1
  18. package/docs/concepts/converge.md +7 -2
  19. package/docs/concepts/durable-runs.md +43 -16
  20. package/docs/concepts/effects.md +133 -50
  21. package/docs/concepts/gates.md +95 -25
  22. package/docs/concepts/machines-and-workflows.md +14 -4
  23. package/docs/concepts/pools-and-sources.md +193 -17
  24. package/docs/concepts/secrets-and-trust.md +186 -62
  25. package/docs/concepts/the-menu.md +14 -0
  26. package/docs/concepts/workspaces.md +135 -56
  27. package/docs/get-started/a-model-decides.md +14 -13
  28. package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
  29. package/docs/get-started/connect-a-model.md +1 -1
  30. package/docs/get-started/create-an-instance.md +17 -14
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
  32. package/docs/get-started/install.md +4 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +72 -15
  34. package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
  35. package/docs/guides/answer-a-gate.md +74 -37
  36. package/docs/guides/bind-open-parts.md +29 -12
  37. package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
  38. package/docs/guides/cancel-a-run.md +26 -16
  39. package/docs/guides/cut-a-worktree.md +560 -0
  40. package/docs/guides/fix-a-failed-converge.md +19 -7
  41. package/docs/guides/give-a-caller-a-scope.md +203 -0
  42. package/docs/guides/give-an-agent-mcp-tools.md +422 -0
  43. package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
  44. package/docs/guides/let-an-agent-write-a-chart.md +14 -7
  45. package/docs/guides/look-inside-a-sandbox.md +115 -42
  46. package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
  47. package/docs/guides/open-the-console.md +63 -14
  48. package/docs/guides/push-and-sign-a-branch.md +67 -21
  49. package/docs/guides/read-a-conversation.md +225 -0
  50. package/docs/guides/run-a-chart-by-name.md +17 -16
  51. package/docs/guides/run-a-command-or-call-a-url.md +84 -41
  52. package/docs/guides/run-one-worker-per-item.md +34 -27
  53. package/docs/guides/set-an-agents-access-and-dials.md +69 -22
  54. package/docs/guides/size-a-workspace.md +19 -6
  55. package/docs/guides/start-a-run-for-each-alert.md +290 -0
  56. package/docs/guides/start-a-run-from-a-run.md +18 -16
  57. package/docs/guides/start-a-run.md +24 -18
  58. package/docs/guides/steer-an-agent.md +332 -0
  59. package/docs/guides/use-a-hosted-provider.md +7 -1
  60. package/docs/guides/watch-a-run.md +146 -117
  61. package/docs/guides/write-a-hook-kind.md +238 -0
  62. package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
  63. package/docs/guides/write-a-workflow.md +18 -8
  64. package/docs/llms.txt +110 -23
  65. package/docs/reference/authoring-surface/agent.md +35 -12
  66. package/docs/reference/authoring-surface/answer.md +20 -4
  67. package/docs/reference/authoring-surface/arg.md +28 -6
  68. package/docs/reference/authoring-surface/basic.md +1 -1
  69. package/docs/reference/authoring-surface/bearer.md +1 -1
  70. package/docs/reference/authoring-surface/branchname.md +15 -0
  71. package/docs/reference/authoring-surface/carry.md +23 -0
  72. package/docs/reference/authoring-surface/chartschema.md +1 -1
  73. package/docs/reference/authoring-surface/chartschemafor.md +1 -1
  74. package/docs/reference/authoring-surface/command.md +37 -22
  75. package/docs/reference/authoring-surface/customize.md +4 -1
  76. package/docs/reference/authoring-surface/defineconfig.md +1 -1
  77. package/docs/reference/authoring-surface/defineevent.md +1 -1
  78. package/docs/reference/authoring-surface/dispatch.md +5 -4
  79. package/docs/reference/authoring-surface/fail.md +18 -5
  80. package/docs/reference/authoring-surface/file.md +37 -17
  81. package/docs/reference/authoring-surface/frompick.md +1 -1
  82. package/docs/reference/authoring-surface/gate.md +118 -0
  83. package/docs/reference/authoring-surface/group-agents.md +15 -0
  84. package/docs/reference/authoring-surface/group-charts.md +16 -0
  85. package/docs/reference/authoring-surface/group-composition.md +25 -0
  86. package/docs/reference/authoring-surface/group-config.md +17 -0
  87. package/docs/reference/authoring-surface/group-effects.md +48 -0
  88. package/docs/reference/authoring-surface/group-failure.md +13 -0
  89. package/docs/reference/authoring-surface/group-gates.md +16 -0
  90. package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
  91. package/docs/reference/authoring-surface/group-steering.md +12 -0
  92. package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
  93. package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
  94. package/docs/reference/authoring-surface/http.md +109 -9
  95. package/docs/reference/authoring-surface/isfailure.md +1 -1
  96. package/docs/reference/authoring-surface/isready.md +14 -0
  97. package/docs/reference/authoring-surface/jr2setup.md +14 -4
  98. package/docs/reference/authoring-surface/mcp.md +107 -0
  99. package/docs/reference/authoring-surface/open.md +41 -8
  100. package/docs/reference/authoring-surface/openbody.md +1 -1
  101. package/docs/reference/authoring-surface/paramsof.md +23 -5
  102. package/docs/reference/authoring-surface/pool.md +79 -11
  103. package/docs/reference/authoring-surface/push.md +31 -13
  104. package/docs/reference/authoring-surface/readyof.md +21 -0
  105. package/docs/reference/authoring-surface/reread.md +19 -0
  106. package/docs/reference/authoring-surface/secret.md +26 -5
  107. package/docs/reference/authoring-surface/sentby.md +1 -1
  108. package/docs/reference/authoring-surface/skip.md +22 -0
  109. package/docs/reference/authoring-surface/source.md +59 -4
  110. package/docs/reference/authoring-surface/steer.md +93 -0
  111. package/docs/reference/authoring-surface/text.md +1 -1
  112. package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
  113. package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
  114. package/docs/reference/authoring-surface/type-agentslots.md +4 -1
  115. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  116. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  117. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  118. package/docs/reference/authoring-surface/type-binding.md +6 -7
  119. package/docs/reference/authoring-surface/type-caller.md +40 -0
  120. package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
  121. package/docs/reference/authoring-surface/type-carry.md +19 -0
  122. package/docs/reference/authoring-surface/type-chainlink.md +3 -3
  123. package/docs/reference/authoring-surface/type-commandinput.md +4 -3
  124. package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
  125. package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
  126. package/docs/reference/authoring-surface/type-customize.md +20 -3
  127. package/docs/reference/authoring-surface/type-delivered.md +17 -0
  128. package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
  129. package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
  130. package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
  131. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  132. package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
  133. package/docs/reference/authoring-surface/type-filebody.md +4 -8
  134. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  135. package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
  136. package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
  137. package/docs/reference/authoring-surface/type-gatelook.md +37 -0
  138. package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
  139. package/docs/reference/authoring-surface/type-gatereply.md +19 -0
  140. package/docs/reference/authoring-surface/type-gateslot.md +21 -0
  141. package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
  142. package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
  143. package/docs/reference/authoring-surface/type-gateslots.md +12 -0
  144. package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
  145. package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
  146. package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
  147. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  148. package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
  149. package/docs/reference/authoring-surface/type-jr2config.md +37 -2
  150. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  151. package/docs/reference/authoring-surface/type-lends.md +5 -1
  152. package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
  153. package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
  154. package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
  155. package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
  156. package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
  157. package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
  158. package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
  159. package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
  160. package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
  161. package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
  162. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  163. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
  164. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  165. package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
  166. package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
  167. package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
  168. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
  169. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
  170. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  171. package/docs/reference/authoring-surface/type-poolspec.md +11 -6
  172. package/docs/reference/authoring-surface/type-pushinput.md +22 -3
  173. package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
  174. package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
  175. package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
  176. package/docs/reference/authoring-surface/type-reposlot.md +6 -6
  177. package/docs/reference/authoring-surface/type-reread.md +18 -0
  178. package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
  179. package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
  180. package/docs/reference/authoring-surface/type-sender.md +26 -1
  181. package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
  182. package/docs/reference/authoring-surface/type-size.md +1 -1
  183. package/docs/reference/authoring-surface/type-skip.md +19 -0
  184. package/docs/reference/authoring-surface/type-slothandles.md +5 -13
  185. package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
  186. package/docs/reference/authoring-surface/type-sourcein.md +25 -0
  187. package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
  188. package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
  189. package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
  190. package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
  191. package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
  192. package/docs/reference/authoring-surface/type-steerargs.md +20 -0
  193. package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
  194. package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
  195. package/docs/reference/authoring-surface/type-workspaced.md +30 -9
  196. package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
  197. package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
  198. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  199. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  200. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  201. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  202. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  203. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  204. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  205. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  206. package/docs/reference/authoring-surface/workspace.md +66 -38
  207. package/docs/reference/authoring-surface/worktree.md +71 -0
  208. package/docs/reference/authoring-surface.md +88 -147
  209. package/docs/reference/cli.md +413 -74
  210. package/docs/reference/config.md +248 -6
  211. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
  212. package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
  213. package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
  214. package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
  215. package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
  216. package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
  217. package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
  218. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
  219. package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
  220. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
  221. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
  222. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
  223. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
  224. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
  225. package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
  226. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
  227. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
  228. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
  229. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
  230. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
  231. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
  232. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
  233. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
  234. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
  235. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
  236. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
  237. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
  238. package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
  239. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
  240. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
  241. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
  242. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
  243. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
  244. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
  245. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
  246. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
  247. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
  248. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
  249. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
  250. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
  251. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
  252. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
  253. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
  254. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
  255. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
  256. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
  257. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
  258. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
  259. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
  260. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
  261. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
  262. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
  263. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
  264. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
  265. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
  266. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  267. package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
  268. package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
  269. package/docs/reference/decisions.md +9 -4
  270. package/docs/reference/glossary.md +138 -45
  271. package/docs/reference/hooks.md +1077 -0
  272. package/docs/reference/http-api.md +988 -123
  273. package/docs/reference/machines.md +0 -1
  274. package/docs/why.md +7 -5
  275. package/package.json +10 -3
  276. package/src/actor.ts +194 -42
  277. package/src/agent.ts +56 -6
  278. package/src/ambient.ts +3 -5
  279. package/src/assembly-checks.ts +530 -4
  280. package/src/callers.ts +245 -0
  281. package/src/chart.ts +238 -26
  282. package/src/charts.ts +9 -2
  283. package/src/config.ts +566 -7
  284. package/src/conversations.ts +453 -0
  285. package/src/custodian.lua +17 -5
  286. package/src/custodian.ts +257 -77
  287. package/src/customize.ts +154 -7
  288. package/src/dispatch-effect.ts +142 -26
  289. package/src/durability.ts +2 -0
  290. package/src/duration.ts +20 -0
  291. package/src/effect-checks.ts +52 -4
  292. package/src/effect.ts +401 -132
  293. package/src/effector-wire.ts +278 -30
  294. package/src/fail.ts +17 -4
  295. package/src/gate-brand.ts +16 -0
  296. package/src/gate.ts +634 -48
  297. package/src/harness-client.ts +84 -35
  298. package/src/harness-env.ts +33 -0
  299. package/src/held-secrets.ts +197 -39
  300. package/src/holds.ts +5 -2
  301. package/src/hook-kinds/discord.ts +92 -0
  302. package/src/hook-kinds/door.ts +107 -0
  303. package/src/hook-kinds/github.ts +89 -0
  304. package/src/hook-kinds/gitlab.ts +72 -0
  305. package/src/hook-kinds/hook.ts +7 -0
  306. package/src/hook-kinds/index.ts +465 -0
  307. package/src/hook-kinds/slack.ts +151 -0
  308. package/src/hook-kinds/webhook.ts +113 -0
  309. package/src/hooks.ts +506 -0
  310. package/src/http-effect.ts +164 -70
  311. package/src/http.ts +933 -99
  312. package/src/index.ts +30 -5
  313. package/src/instance.ts +196 -26
  314. package/src/internal.ts +23 -3
  315. package/src/kube-client.ts +6 -3
  316. package/src/machine-doc.ts +29 -5
  317. package/src/mcp-effect.ts +424 -0
  318. package/src/model-keys.ts +24 -0
  319. package/src/mount-digest.ts +41 -7
  320. package/src/names.ts +82 -7
  321. package/src/notices.ts +576 -59
  322. package/src/open-body.ts +110 -30
  323. package/src/open-effect.ts +17 -4
  324. package/src/open.ts +40 -7
  325. package/src/parts.ts +125 -35
  326. package/src/party.ts +35 -1
  327. package/src/pick.ts +43 -9
  328. package/src/pool.ts +1172 -172
  329. package/src/registration.ts +249 -17
  330. package/src/repo-identity.ts +7 -3
  331. package/src/repos.ts +20 -2
  332. package/src/run-host.ts +1614 -190
  333. package/src/run-list.ts +191 -0
  334. package/src/sandbox-effect.ts +217 -66
  335. package/src/sandbox-kube.ts +116 -21
  336. package/src/secret-placement.ts +248 -35
  337. package/src/server.ts +53 -4
  338. package/src/setup.ts +158 -38
  339. package/src/snapshot-store.ts +565 -48
  340. package/src/steer.ts +111 -0
  341. package/src/stub-harness.ts +6 -2
  342. package/src/tokens.ts +362 -46
  343. package/src/vocabulary.ts +23 -4
  344. package/src/wire.ts +27 -10
  345. package/src/workspace.ts +314 -259
  346. package/src/worktree.ts +249 -0
  347. package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
  348. package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
  349. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
  350. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
@@ -8,10 +8,10 @@ description: How a Workflow gets a Sandbox with a git Worktree or a shared folde
8
8
  A [Workspace](../reference/glossary.md#workspace) is a [Machine](../reference/glossary.md#machine) that runs a body
9
9
  Machine in a [Sandbox](../reference/glossary.md#sandbox). The Sandbox is a pod of its own, with a folder, `/work`, that
10
10
  the Agents and Effects of the body share. When the Workspace names [Repos](../reference/glossary.md#repo), `/work` holds
11
- a git [Worktree](../reference/glossary.md#worktree) of each Repo on one branch. A Workspace that names no Repo has only
12
- the folder (see [A Workspace with no Repo](#a-workspace-with-no-repo)). You make a Workspace with
13
- [`workspace()`](../reference/authoring-surface/workspace.md). The Workspace makes the Sandbox when the run enters it,
14
- and deletes the Sandbox when the body reaches a final state.
11
+ a clone of each Repo. The body cuts each git [Worktree](../reference/glossary.md#worktree) that it needs. A Workspace
12
+ that names no Repo has only the folder (see [A Workspace with no Repo](#a-workspace-with-no-repo)). You make a Workspace
13
+ with [`workspace()`](../reference/authoring-surface/workspace.md). The Workspace makes the Sandbox when the run enters
14
+ it, and deletes the Sandbox when the body reaches a final state.
15
15
 
16
16
  ## Why Workspaces exist
17
17
 
@@ -27,14 +27,24 @@ Agent.
27
27
  ## A Workspace and its body
28
28
 
29
29
  This example is a [Workflow](../reference/glossary.md#workflow). A coder changes the `app` Repo, and reads the
30
- `handbook` Repo beside it. Then a person approves the change at a [Gate](../reference/glossary.md#gate), and the Machine
31
- pushes the branch:
30
+ `handbook` Repo beside it. The Machine first cuts the coder's Worktree. Then a person approves the change at a
31
+ [Gate](../reference/glossary.md#gate), and the Machine pushes the branch:
32
32
 
33
33
  **Anthropic**
34
34
 
35
35
  ```ts
36
36
  import { z } from "zod";
37
- import { agent, defineEvent, fail, jr2Setup, push, workspace, type Workspaced } from "@jr2/orchestrator";
37
+ import {
38
+ agent,
39
+ defineEvent,
40
+ fail,
41
+ isReady,
42
+ jr2Setup,
43
+ push,
44
+ workspace,
45
+ worktree,
46
+ type Workspaced,
47
+ } from "@jr2/orchestrator";
38
48
 
39
49
  const finished = defineEvent({
40
50
  name: "finished",
@@ -51,6 +61,10 @@ const approve = defineEvent({
51
61
  const runInput = z.object({ ticket: z.string(), prompt: z.string() });
52
62
  type BodyInput = Workspaced<z.infer<typeof runInput>, "app" | "handbook">;
53
63
 
64
+ // The folder of the Worktree, and its branch. The Machine knows both before the cut.
65
+ const fixTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
66
+ const branchOf = (context: BodyInput) => `docs/${context.ticket}`;
67
+
54
68
  const body = jr2Setup({
55
69
  types: {} as { context: BodyInput; input: BodyInput },
56
70
  events: [finished, approve],
@@ -59,6 +73,7 @@ const body = jr2Setup({
59
73
  model: "anthropic/claude-haiku-5-5",
60
74
  instructions: "You are a careful engineer. You commit your work on the branch.",
61
75
  }),
76
+ cut: worktree(),
62
77
  // Two Repo Slots, so the push names the slot that it pushes.
63
78
  publish: push({ slot: "app" }),
64
79
  fail: fail(),
@@ -66,17 +81,25 @@ const body = jr2Setup({
66
81
  }).createMachine({
67
82
  id: "docs-fix",
68
83
  context: ({ input }) => input,
69
- initial: "working",
84
+ initial: "cutting",
70
85
  // The Sandbox is gone, and its unpushed commits with it. This body fails the run.
71
86
  on: { "agent.fault": ".failed", "workspace.lost": ".lost" },
72
87
  states: {
88
+ // The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
89
+ cutting: {
90
+ invoke: {
91
+ src: "cut",
92
+ input: ({ context }) => ({ path: fixTree(context), branch: branchOf(context) }),
93
+ onDone: [{ guard: ({ event }) => isReady(event.output), target: "working" }, { target: "notCut" }],
94
+ },
95
+ },
73
96
  working: {
74
97
  invoke: {
75
98
  src: "coder",
76
99
  input: ({ context }) => ({
77
- prompt: `${context.prompt}\nRead the style rules in ${context.workspace.repos.handbook}.`,
78
- // The Agent works in the Worktree of the app slot.
79
- cwd: context.workspace.repos.app,
100
+ // A slot that is only read needs no cut: its `default` folder is on the Binding's base.
101
+ prompt: `${context.prompt}\nRead the style rules in ${context.workspace.repos.handbook}/default.`,
102
+ cwd: fixTree(context),
80
103
  }),
81
104
  },
82
105
  on: { finished: "review" },
@@ -86,7 +109,7 @@ const body = jr2Setup({
86
109
  invoke: {
87
110
  src: "gate",
88
111
  input: ({ context }) => ({
89
- meta: { branch: context.workspace.branch, worktree: context.workspace.repos.app },
112
+ meta: { branch: branchOf(context), worktree: fixTree(context) },
90
113
  }),
91
114
  },
92
115
  on: { approve: "publishing" },
@@ -96,11 +119,14 @@ const body = jr2Setup({
96
119
  publishing: {
97
120
  invoke: {
98
121
  src: "publish",
122
+ // The Worktree names what to push: jr2 reads its branch from the cut.
123
+ input: ({ context }) => ({ worktree: fixTree(context) }),
99
124
  onDone: [{ guard: ({ event }) => event.output.outcome === "pushed", target: "done" }, { target: "review" }],
100
125
  },
101
126
  },
102
127
  done: { type: "final" },
103
128
  // Not final states: a final state means `done`. Each one fails the run, and the run ends `failed`.
129
+ notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
104
130
  lost: { invoke: { src: "fail", input: { reason: "the Sandbox was lost" } } },
105
131
  failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
106
132
  },
@@ -115,7 +141,6 @@ export const machine = workspace(body, {
115
141
  app: { url: "https://github.com/you/app.git", ref: "main" },
116
142
  handbook: "https://github.com/you/handbook.git",
117
143
  },
118
- spec: ({ input }) => ({ branch: `docs/${input.ticket}` }),
119
144
  });
120
145
  ```
121
146
 
@@ -123,7 +148,17 @@ export const machine = workspace(body, {
123
148
 
124
149
  ```ts
125
150
  import { z } from "zod";
126
- import { agent, defineEvent, fail, jr2Setup, push, workspace, type Workspaced } from "@jr2/orchestrator";
151
+ import {
152
+ agent,
153
+ defineEvent,
154
+ fail,
155
+ isReady,
156
+ jr2Setup,
157
+ push,
158
+ workspace,
159
+ worktree,
160
+ type Workspaced,
161
+ } from "@jr2/orchestrator";
127
162
 
128
163
  const finished = defineEvent({
129
164
  name: "finished",
@@ -140,6 +175,10 @@ const approve = defineEvent({
140
175
  const runInput = z.object({ ticket: z.string(), prompt: z.string() });
141
176
  type BodyInput = Workspaced<z.infer<typeof runInput>, "app" | "handbook">;
142
177
 
178
+ // The folder of the Worktree, and its branch. The Machine knows both before the cut.
179
+ const fixTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
180
+ const branchOf = (context: BodyInput) => `docs/${context.ticket}`;
181
+
143
182
  const body = jr2Setup({
144
183
  types: {} as { context: BodyInput; input: BodyInput },
145
184
  events: [finished, approve],
@@ -148,6 +187,7 @@ const body = jr2Setup({
148
187
  model: "local/qwen3.6-35b-a3b",
149
188
  instructions: "You are a careful engineer. You commit your work on the branch.",
150
189
  }),
190
+ cut: worktree(),
151
191
  // Two Repo Slots, so the push names the slot that it pushes.
152
192
  publish: push({ slot: "app" }),
153
193
  fail: fail(),
@@ -155,17 +195,25 @@ const body = jr2Setup({
155
195
  }).createMachine({
156
196
  id: "docs-fix",
157
197
  context: ({ input }) => input,
158
- initial: "working",
198
+ initial: "cutting",
159
199
  // The Sandbox is gone, and its unpushed commits with it. This body fails the run.
160
200
  on: { "agent.fault": ".failed", "workspace.lost": ".lost" },
161
201
  states: {
202
+ // The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
203
+ cutting: {
204
+ invoke: {
205
+ src: "cut",
206
+ input: ({ context }) => ({ path: fixTree(context), branch: branchOf(context) }),
207
+ onDone: [{ guard: ({ event }) => isReady(event.output), target: "working" }, { target: "notCut" }],
208
+ },
209
+ },
162
210
  working: {
163
211
  invoke: {
164
212
  src: "coder",
165
213
  input: ({ context }) => ({
166
- prompt: `${context.prompt}\nRead the style rules in ${context.workspace.repos.handbook}.`,
167
- // The Agent works in the Worktree of the app slot.
168
- cwd: context.workspace.repos.app,
214
+ // A slot that is only read needs no cut: its `default` folder is on the Binding's base.
215
+ prompt: `${context.prompt}\nRead the style rules in ${context.workspace.repos.handbook}/default.`,
216
+ cwd: fixTree(context),
169
217
  }),
170
218
  },
171
219
  on: { finished: "review" },
@@ -175,7 +223,7 @@ const body = jr2Setup({
175
223
  invoke: {
176
224
  src: "gate",
177
225
  input: ({ context }) => ({
178
- meta: { branch: context.workspace.branch, worktree: context.workspace.repos.app },
226
+ meta: { branch: branchOf(context), worktree: fixTree(context) },
179
227
  }),
180
228
  },
181
229
  on: { approve: "publishing" },
@@ -185,11 +233,14 @@ const body = jr2Setup({
185
233
  publishing: {
186
234
  invoke: {
187
235
  src: "publish",
236
+ // The Worktree names what to push: jr2 reads its branch from the cut.
237
+ input: ({ context }) => ({ worktree: fixTree(context) }),
188
238
  onDone: [{ guard: ({ event }) => event.output.outcome === "pushed", target: "done" }, { target: "review" }],
189
239
  },
190
240
  },
191
241
  done: { type: "final" },
192
242
  // Not final states: a final state means `done`. Each one fails the run, and the run ends `failed`.
243
+ notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
193
244
  lost: { invoke: { src: "fail", input: { reason: "the Sandbox was lost" } } },
194
245
  failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
195
246
  },
@@ -204,28 +255,26 @@ export const machine = workspace(body, {
204
255
  app: { url: "https://github.com/you/app.git", ref: "main" },
205
256
  handbook: "https://github.com/you/handbook.git",
206
257
  },
207
- spec: ({ input }) => ({ branch: `docs/${input.ticket}` }),
208
258
  });
209
259
  ```
210
260
 
211
261
  [Agents and Turns](agents-and-turns.md) tells how to define an Agent.
212
262
 
213
263
  The options of `workspace()` say what the Sandbox is. [`jr2 up`](../reference/cli.md#up) reads them before a run starts.
214
- Only `spec` and a per-run [Repo Slot](../reference/glossary.md#repo-slot) read the run input:
264
+ Only a per-run [Repo Slot](../reference/glossary.md#repo-slot) reads the run input:
215
265
 
216
266
  - `input` is the schema of the run input: what [`jr2 run`](../reference/cli.md#run) gives with `--input`. Put it on the
217
267
  `workspace()`, not on the body.
218
268
  - `repos` gives the Repo Slots that the Sandbox clones. Leave it out for a Workspace with no Repo.
219
- - `spec` is a function from the run input to the branch. Its input also holds `instanceId` (see
220
- [`HostInjectedInput`](../reference/authoring-surface/type-hostinjectedinput.md)).
221
269
  - `resources` is the [Size](../reference/glossary.md#size). `image`, `user` and `effector` name the images of the pod's
222
270
  containers.
223
271
  - `secrets` names the [Held secrets](../reference/glossary.md#held-secret) that the Sandbox can use (see
224
272
  [Secrets and trust](secrets-and-trust.md)).
273
+ - `workGroup` is the group id that owns the files in `/work`. The default is 2000.
225
274
 
226
275
  The body gets the run input, plus `workspace`: the handles of the Workspace (see
227
- [`WorkspaceHandles`](../reference/authoring-surface/type-workspacehandles.md)). `workspace.repos` gives the absolute
228
- path of each Worktree, and `workspace.branch` gives the branch. Give a path to an Agent's
276
+ [`WorkspaceHandles`](../reference/authoring-surface/type-workspacehandles.md)). `workspace.repos` gives the folder of
277
+ each Repo Slot, `/work/<slot>`. This folder is not a Worktree. Give the path of a Worktree to an Agent's
229
278
  [Turn](../reference/glossary.md#turn) as its `cwd`, as the example does. Type the body's input with
230
279
  [`Workspaced`](../reference/authoring-surface/type-workspaced.md), also when the run input is `z.object({})`.
231
280
 
@@ -243,7 +292,8 @@ A Repo Slot is a key of the `repos` option. The key is your Machine's name for t
243
292
  Repo under `/work`. Each slot is one of three kinds:
244
293
 
245
294
  - **Bound**: the Machine gives the url, or a [Binding](../reference/glossary.md#binding) `{ url, ref }`. `ref` is the
246
- branch or the commit that the Worktree starts from. Without `ref`, the Worktree starts from the Repo's default branch.
295
+ branch, the tag or the commit that the clone stays on, and a new branch starts from it. Without `ref`, it is the
296
+ Repo's default branch.
247
297
  - **[Open](../reference/glossary.md#open)**: the Machine gives [`open`](../reference/authoring-surface/open.md). The
248
298
  [Instance](../reference/glossary.md#instance) that registers the Machine binds the slot with
249
299
  [`customize()`](../reference/authoring-surface/customize.md) (see [Composition](composition.md)).
@@ -251,7 +301,7 @@ Repo under `/work`. Each slot is one of three kinds:
251
301
 
252
302
  A Machine that names no slot writes `repos: open` for the whole map. Then the composer names each slot.
253
303
  `workspace.repos` keeps the slots in the order that `repos` declares them. jr2 gives no slot a special meaning. Your
254
- Machine says which Worktree each Agent works in.
304
+ Machine says where each Agent works.
255
305
 
256
306
  The cluster keeps one read-only cache of each Repo on each node. A Sandbox clones from that cache, and a fetch in the
257
307
  pod goes through it. [Architecture](architecture.md) tells about the Repo cache. Run
@@ -259,23 +309,44 @@ pod goes through it. [Architecture](architecture.md) tells about the Repo cache.
259
309
 
260
310
  ## The folders in the Sandbox
261
311
 
262
- Each Repo Slot gets a folder under `/work`. The clone of the Repo is `/work/<slot>/default/`. The Worktree of the branch
263
- is beside it, at `/work/<slot>/<branch>`. In the folder name, each `/` of the branch becomes `-`. For example, the
264
- branch `docs/T-12` of the slot `app` is at `/work/app/docs-T-12`. This is the
265
- [Project layout](../reference/glossary.md#project-layout).
312
+ Each Repo Slot gets a folder under `/work`. The clone of the Repo is `/work/<slot>/default/`. It stays on the base of
313
+ the Binding: a branch is attached, and a tag or a commit is detached. No work happens in `default/`. A slot that an
314
+ Agent only reads needs nothing more: the Agent reads `/work/<slot>/default`. This is the
315
+ [Project layout](../reference/glossary.md#project-layout). `/work/.jr2/` is reserved for jr2.
316
+
317
+ The Workspace only clones. Your Machine cuts each Worktree with the
318
+ [`worktree()`](../reference/authoring-surface/worktree.md) Effect, in a state of the body. You give the path, and the
319
+ branch or the commit:
320
+
321
+ - `{ path: "/work/app/fix", branch: "docs/123" }` cuts a branch Worktree. The cut finds a local branch, then
322
+ `origin/<branch>`, tracked, after a fetch. With neither, it makes a new branch from `from` or the base of the Binding.
323
+ - `{ path: "/work/app/review", detach: sha }` cuts a detached Worktree for a reviewer. Each cut forces it to the commit
324
+ and cleans it. Ignored files stay, so that the reviewer can run the tests.
325
+
326
+ The path is exactly `/work/<slot>/<name>`. The name is one segment that is not `default` and starts with neither `.` nor
327
+ `-`. So your Machine knows the path before the cut, and the folder name does not change with the branch name.
328
+
329
+ The output of a cut is `ready`, `refused` or `unknown`. A later outcome can come, so route the last arm of `onDone` with
330
+ no guard. A bad path or a bad branch name is `refused`, and nothing changes. A cut is idempotent: the same cut again
331
+ reads the same Worktree, and its `head` is the tip of the branch.
332
+
333
+ `worktree({ expect: "new" })` refuses a branch that exists. `worktree({ expect: "existing" })` refuses a branch that
334
+ does not exist, and makes nothing.
335
+
336
+ The author chooses how many Agents work in a Workspace. Agents in one Worktree share git, as people in one checkout do.
337
+ Each coder that needs its own branch gets its own Worktree of the one clone.
266
338
 
267
- `spec` returns a [`WorkspaceSpec`](../reference/authoring-surface/type-workspacespec.md). It gives the branch, and two
268
- optional fields:
339
+ Your Machine chooses the branch name. Give each run a different branch, so that two runs do not push to one branch. Make
340
+ it from a field of the run input, or from `instanceId` when the run input has no field that is different for each run.
341
+ Only a root `workspace()` gets `instanceId` from jr2. A `workspace()` that a different Machine invokes gets it only when
342
+ its parent gives it.
269
343
 
270
- - `reviewSha`: a commit for a reviewer. jr2 checks it out in a second Worktree, at `/work/<slot>/<branch>-review`. The
271
- body gets its paths as `workspace.review`.
272
- - `workGroup`: the group id that owns the files in `/work`. The default is 2000.
344
+ An Agent, a `command`, a `push`, and an `http` Effect with a file body each name their Worktree by its path: `cwd` for a
345
+ Turn, `worktree` for an Effect. A `push` has no branch in its input. jr2 reads the branch from the cut of that path. So
346
+ an Agent that moves `HEAD` does not change what is pushed.
273
347
 
274
- Your Machine chooses the branch name. jr2 makes the branch in each Repo Slot. Give each run a different branch, so that
275
- two runs do not push to one branch. Make it from a field of the run input, or from `instanceId` when the run input has
276
- no field that is different for each run: ``spec: ({ input }) => ({ branch: `fix/${input.instanceId}` })``. Only a root
277
- `workspace()` gets `instanceId` from jr2. A `workspace()` that a different Machine invokes gets it only when its parent
278
- gives it.
348
+ jr2 has no Effect that removes a Worktree. To remove one, run `git worktree remove` in a `command`.
349
+ [Cut a Worktree](../guides/cut-a-worktree.md) gives the procedure.
279
350
 
280
351
  ## A Workspace with no Repo
281
352
 
@@ -504,15 +575,16 @@ export const machine = workspace(body, { input: runInput });
504
575
 
505
576
  In a Workspace with no Repo:
506
577
 
507
- - Type the body's input as `Workspaced<RunInput, never>`. The handles are `{ repos: {} }`, with no branch.
578
+ - Type the body's input as `Workspaced<RunInput, never>`. The handles are `{ repos: {} }`.
508
579
  - An Agent's Turn that gives no `cwd` works in `/work`. The Agent's `workspace` access is the same as in each Workspace:
509
580
  `"write"` gives it `write`, `edit` and `bash`, and `"read"` gives it `bash` only.
510
581
  - A `command` with no Effect secret runs in `/work`.
511
582
  - An `http` Effect sends a file of `/work` with `file(path)`. The path is relative to `/work`.
512
- - `spec` is optional, and gives only `workGroup`.
513
- - A `push` needs a branch, and a `command` with an Effect secret runs on a commit. Thus `jr2 up` refuses both.
583
+ - A `worktree()` needs a Repo Slot, a `push` needs a branch, and a `command` with an Effect secret runs on a commit.
584
+ Thus `jr2 up` refuses all three.
514
585
 
515
- jr2 deletes `/work` with the Sandbox. Send what must stay before the body ends, as the example does with `upload`. A
586
+ jr2 deletes `/work` with the Sandbox. Send what must stay before the body ends, as the example does with `upload`. The
587
+ secret `files` that `upload` names is an entry of the `secrets` list of `jr2.config.ts`, as each Effect secret is. A
516
588
  Lost Sandbox has lost all of its work, because no commit was pushed.
517
589
 
518
590
  ## The pod
@@ -529,10 +601,10 @@ A Sandbox pod has two containers, and two more when the Machine needs them:
529
601
  ### The Sandbox Image
530
602
 
531
603
  The [Sandbox Image](../reference/glossary.md#sandbox-image) is the image of the Harness container. It holds the tools
532
- that an Agent's Working tools can use, and the shell that you get when you run `kubectl exec` into the pod. The image is
533
- yours. Its floor is glibc, git and a writable `HOME`. [Add tools to the Sandbox](../guides/add-tools-to-the-sandbox.md)
534
- gives the full list. jr2 adds nothing to the image. It mounts its own runtime into the pod at `/opt/jr2`, and starts the
535
- Harness from there. Thus the `ENTRYPOINT` and the `CMD` of the image do not run.
604
+ that an Agent's Working tools can use, and the shell that you get with `jr2 workspace shell`. The image is yours. Its
605
+ floor is glibc, git and a writable `HOME`. [Add tools to the Sandbox](../guides/add-tools-to-the-sandbox.md) gives the
606
+ full list. jr2 adds nothing to the image. It mounts its own runtime into the pod at `/opt/jr2`, and starts the Harness
607
+ from there. Thus the `ENTRYPOINT` and the `CMD` of the image do not run.
536
608
 
537
609
  `image` names it in one of two forms:
538
610
 
@@ -593,8 +665,7 @@ A Workspace goes through these states:
593
665
 
594
666
  1. `placing`: jr2 writes the Sandbox, and waits for a node.
595
667
  2. `provisioning`: the pod starts.
596
- 3. `attaching`: the Harness clones each Repo Slot and makes its Worktree. With no Repo Slot, this state has nothing to
597
- do.
668
+ 3. `attaching`: the Harness clones each Repo Slot. With no Repo Slot, this state has nothing to do.
598
669
  4. `running`: the body runs.
599
670
  5. `teardown`: the body is in a final state, and jr2 deletes the Sandbox.
600
671
  6. `failing`: the body invoked [`fail`](../reference/authoring-surface/fail.md), and jr2 deletes the Sandbox. Then the
@@ -643,29 +714,37 @@ the Harness container does not lose the Workspace: the pod and `/work` stay.
643
714
  - A per-run url must match an entry of [`git.credentials`](../reference/config.md#git-credentials). jr2 refuses the url
644
715
  before it makes the Sandbox when no entry matches it. Thus a run cannot send the cluster's credentials to a host that
645
716
  it chooses.
646
- - `spec` returns a `branch`. When a run enters the Workspace, it faults if `spec` gives no `branch`, or the branch
647
- `default`, because `default` is the folder of the clone. With no Repo Slot, it faults if `spec` gives a `branch` or a
648
- `reviewSha`.
649
- - With no Repo Slot, `jr2 up` refuses a `push`, and a `command` that names an Effect secret.
717
+ - `jr2 up` refuses a Repo Slot name that starts with `.`, because `/work/.jr2/` is reserved for jr2.
718
+ - A cut of the branch that `default/` holds is `refused`, and the reason names `/work/<slot>/default`.
719
+ - With no Repo Slot, `jr2 up` refuses a `worktree()`, a `push`, and a `command` that names an Effect secret.
650
720
  - A Size has only `limits.memory` and `limits.cpu`. jr2 refuses `requests` and each other key.
651
721
  - `jr2 up` refuses a Size that leaves the Harness container less than 256Mi of memory, or no cpu.
652
- - Under a Workspace with more than one Repo Slot, a Turn states its `cwd`, and a `command` and a `push` name their slot.
722
+ - Under a Workspace with Repo Slots, a Turn states its `cwd`, and a `command`, a `push` and an `http` Effect with a file
723
+ body state their `worktree`. A Turn that wants `/work` states `cwd: "/work"`. An Agent with `workspace: "none"` needs
724
+ no `cwd`.
725
+ - A `push`, a `command` that names an Effect secret, and an `http` Effect with a file body need a Worktree that a cut of
726
+ this run made.
727
+ - Under a Workspace with more than one Repo Slot, a `push` names its `slot`.
653
728
 
654
729
  The guides and the Reference give the refusal text of each rule.
655
730
 
656
731
  ## Read next
657
732
 
733
+ - [Cut a Worktree](../guides/cut-a-worktree.md)
658
734
  - [Look inside a Sandbox](../guides/look-inside-a-sandbox.md)
659
735
  - [Add tools to the Sandbox](../guides/add-tools-to-the-sandbox.md)
660
736
  - [Size a Workspace](../guides/size-a-workspace.md)
661
737
  - [Give the cluster git credentials](../guides/give-the-cluster-git-credentials.md)
662
738
  - [Effects](effects.md), [Composition](composition.md), [Architecture](architecture.md)
663
739
  - Reference: [`workspace`](../reference/authoring-surface/workspace.md),
740
+ [`worktree`](../reference/authoring-surface/worktree.md),
664
741
  [`RepoSlot`](../reference/authoring-surface/type-reposlot.md), [`Size`](../reference/authoring-surface/type-size.md),
665
742
  [`UserContainer`](../reference/authoring-surface/type-usercontainer.md),
666
743
  [`workspace.lost`](../reference/mechanism-events.md#workspace-lost), [`sandbox`](../reference/config.md#sandbox)
667
744
  - [ADR-0012](../reference/decisions/0012-workspace-wrapper-machine.md): `workspace()` wraps a body Machine and owns only
668
745
  Sandbox lifecycle
746
+ - [ADR-0075](../reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md): a Machine cuts a
747
+ Worktree, and the Workspace only clones
669
748
  - [ADR-0051](../reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md): a Repo is a slot
670
749
  on the `workspace()` and a cache on the node
671
750
  - [ADR-0060](../reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md): a Workspace states
@@ -406,8 +406,7 @@ there:
406
406
  ```text
407
407
  …
408
408
  → context kind-jr2 / namespace my-instance
409
- run 8fecb896-2100-4ee6-88d6-788eaf0d7244 (triage) — attached
410
- → active "triaging"
409
+ run f40a189e-b554-4310-b6e5-3b92c74e414e (triage) — attached
411
410
  → active "triaging"
412
411
  → active "clarify"
413
412
  ```
@@ -427,10 +426,10 @@ can show the same state. `jr2 run` does not return while the run waits. Keep it
427
426
  > ```text
428
427
  > …
429
428
  > → context kind-jr2 / namespace my-instance
430
- > run 89cc6ea5-9e2d-4f8a-8d02-18a6e47f2a5c (triage) — attached
429
+ > run 4eafe1a5-b45d-4613-8ae3-4c85b26eeb0c (triage) — attached
431
430
  > → active "triaging"
432
431
  > → done "accepted"
433
- > {"runId":"89cc6ea5-9e2d-4f8a-8d02-18a6e47f2a5c","workflow":"triage","instanceId":"fac049b9-9dd2-4ba5-aca8-0b3ebe619701","status":"done","value":"accepted","context":{"request":"Add a --version flag to the CLI. It prints the installed version and exits.","exchange":[],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"assembly":"3eb6cd4d61bf","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
432
+ > {"runId":"4eafe1a5-b45d-4613-8ae3-4c85b26eeb0c","workflow":"triage","instanceId":"d4daa575-f8ac-4ba9-af2e-406084adbd89","startedAt":"2026-11-06T19:24:22.161Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"done","value":"accepted","context":{"request":"Add a --version flag to the CLI. It prints the installed version and exits.","exchange":[],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"assembly":"9ff0a4d3c5da","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
434
433
  > ```
435
434
  >
436
435
  > Your run id and your [Instance ID](../reference/glossary.md#instance-id) are different, and so is the `summary`, the
@@ -444,14 +443,15 @@ The Gate holds the question of the Agent in its `meta`. In the send terminal, ru
444
443
  jr2 status <run id>
445
444
  ```
446
445
 
447
- `<run id>` is the run id that `jr2 run` printed. You can give its first 4 characters or more, such as `8fecb896`.
446
+ `<run id>` is the run id that `jr2 run` printed. You can give its first 4 characters or more, such as `f40a189e`.
448
447
  [`jr2 status`](../reference/cli.md#status) prints the status of the run as one line of JSON. The Gate is in `gates`, at
449
448
  the end:
450
449
 
451
450
  ```text
452
451
  …
453
452
  → context kind-jr2 / namespace my-instance
454
- {"runId":"8fecb896-2100-4ee6-88d6-788eaf0d7244","workflow":"triage","instanceId":"a814d943-9b27-410c-a790-2c4225aa05ba","status":"active","value":"clarify","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where specifically should the version number be displayed (e.g., CLI flag, About screen, or help output), and what exact user action should trigger it?"}]},"children":[],"waiting":[],"held":[],"assembly":"3eb6cd4d61bf","gates":[{"gate":"clarify","path":["clarify"],"accepts":[{"name":"answer","description":"Answer the triager's question. It triages the request again with the answer in hand.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"info":{"type":"string","description":"The answer, in prose."}},"required":["info"],"additionalProperties":false}}],"acceptsNow":["answer"],"meta":{"question":"Where specifically should the version number be displayed (e.g., CLI flag, About screen, or help output), and what exact user action should trigger it?"}}]}
453
+ {"runId":"f40a189e-b554-4310-b6e5-3b92c74e414e","workflow":"triage","instanceId":"6a6fb1c4-d6d8-4867-a51b-693c0772cc7b","startedAt":"2026-11-06T19:23:30.814Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"active","value":"clarify","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where should the version number be displayed to users (e.g., a specific command like `--version`, an 'About' screen, or a startup log message)?"}]},"children":[],"waiting":[],"held":[],"assembly":"9ff0a4d3c5da","gates":[{"gate":"clarify","path":["clarify"],"accepts":[{"name":"answer","description":"Answer the triager's question. It triages the request again with the answer in hand.","input":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"info":{"type":"string","description":"The answer, in prose."}},"required":["info"],"additionalProperties":false}}],"acceptsNow":["answer"],"meta":{"question":"Where should the version number be displayed to users (e.g., a specific command like `--version`, an 'About' screen, or a startup log message)?"}}]}
454
+ started by: token/instance on behalf of kubernetes-admin
455
455
  ```
456
456
 
457
457
  The `question` is in the words of the model, so your question is different. Your instance id is different too. The Gate
@@ -470,7 +470,7 @@ jr2 send <run id> --gate clarify --event answer --input '{"info":"Add a --versio
470
470
  ```text
471
471
  …
472
472
  → context kind-jr2 / namespace my-instance
473
- delivered answer to gate "clarify" on 8fecb896-2100-4ee6-88d6-788eaf0d7244
473
+ delivered answer to gate "clarify" on f40a189e-b554-4310-b6e5-3b92c74e414e
474
474
  ```
475
475
 
476
476
  The run goes back to `triaging`, and the Agent reads your answer. Look at the first terminal. When the Agent picks
@@ -481,7 +481,7 @@ The run goes back to `triaging`, and the Agent reads your answer. Look at the fi
481
481
  → active "triaging"
482
482
  → active "triaging"
483
483
  → done "accepted"
484
- {"runId":"8fecb896-2100-4ee6-88d6-788eaf0d7244","workflow":"triage","instanceId":"a814d943-9b27-410c-a790-2c4225aa05ba","status":"done","value":"accepted","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where specifically should the version number be displayed (e.g., CLI flag, About screen, or help output), and what exact user action should trigger it?","info":"Add a --version flag to the CLI. It prints the installed version and exits."}],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"assembly":"3eb6cd4d61bf","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
484
+ {"runId":"f40a189e-b554-4310-b6e5-3b92c74e414e","workflow":"triage","instanceId":"6a6fb1c4-d6d8-4867-a51b-693c0772cc7b","startedAt":"2026-11-06T19:23:30.814Z","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"},"status":"done","value":"accepted","context":{"request":"People keep asking what version they are running.","exchange":[{"question":"Where should the version number be displayed to users (e.g., a specific command like `--version`, an 'About' screen, or a startup log message)?","info":"Add a --version flag to the CLI. It prints the installed version and exits."}],"summary":"Add a --version flag to the CLI that prints the installed version and exits."},"children":[],"waiting":[],"held":[],"answers":[{"gate":"clarify","type":"answer","at":"2026-11-06T19:24:02.957Z","answeredBy":{"via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}}],"assembly":"9ff0a4d3c5da","output":{"outcome":"accepted","summary":"Add a --version flag to the CLI that prints the installed version and exits."}}
485
485
  ```
486
486
 
487
487
  `summary` is the request as the model wrote it again, in one sentence. Your summary is different. The `output` of the
@@ -502,24 +502,25 @@ This request does not say what is wrong. The run goes to `clarify` and waits, as
502
502
  ```text
503
503
  …
504
504
  → context kind-jr2 / namespace my-instance
505
- run 84079cb2-3cdd-4b9a-ae39-51ab873c5f3c (triage) — attached
505
+ run 7e6c9deb-ca65-4103-b517-abf6b0028aff (triage) — attached
506
506
  → active "triaging"
507
507
  → active "clarify"
508
508
  ```
509
509
 
510
- `84079cb2-3cdd-4b9a-ae39-51ab873c5f3c` is the id of the second run. Your run id is different. If the Agent picks
510
+ `7e6c9deb-ca65-4103-b517-abf6b0028aff` is the id of the second run. Your run id is different. If the Agent picks
511
511
  `accept` or `reject`, start the run again.
512
512
 
513
513
  ## Answer from the Console
514
514
 
515
- Open `http://localhost:4000` in your browser. If the badge after the `instance token` box shows `live`, go to the next
516
- step. If the box is empty, get the token. In the send terminal, run:
515
+ Open `http://localhost:4000` in your browser. If the nav shows `as token/instance` and the badge `live`, go to the next
516
+ step. If the nav shows the `Caller token` box, get the token. In the send terminal, run:
517
517
 
518
518
  ```sh
519
519
  kubectl -n my-instance get secret jr2-instance -o jsonpath='{.data.JR2_INSTANCE_TOKEN}' | base64 -d
520
520
  ```
521
521
 
522
- Type the token in the `instance token` box. Push Enter. The badge after the box shows `live`.
522
+ Type the token in the `Caller token` box. Push Enter. The badge after the box shows `live`, and the nav shows
523
+ `as token/instance`.
523
524
 
524
525
  Click `triage` in the Fleet rail on the left. The center shows the Machine of `triage`, with its states and transitions.
525
526
  Click the run in the rail. The Console lights the state of the run, `clarify`.