@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
@@ -15,7 +15,7 @@ Sandbox Image: a Machine names those itself.
15
15
  Every field is optional, and an Instance can run with no `jr2.config.ts`. Write the file as
16
16
  `export default defineConfig({ … })`. When the config loads, a file with no default export is
17
17
  refused: `` <file> has no default export (use `export default defineConfig({…})`) ``. The load also
18
- checks the shape of `git.credentials`, `secrets`, `harness.catalog` and `priorityClasses`. It
18
+ checks the shape of `git.credentials`, `secrets`, `harness.catalog`, `priorityClasses` and `mcp`. It
19
19
  checks that `sandbox` is an object, and checks its `resources`. Each refusal names the field.
20
20
  `jr2 up` checks the rest.
21
21
 
@@ -42,7 +42,7 @@ export default defineConfig({ name: "my-instance", harness });
42
42
  - [Secrets and trust](../concepts/secrets-and-trust.md)
43
43
  - [ADR-0050: What code names, config declares; what the CLI names, discovery finds](decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md)
44
44
 
45
- Fields: [`name`](#name), [`secrets`](#secrets), [`git`](#git), [`harness`](#harness), [`sandbox`](#sandbox), [`priorityClasses`](#priorityclasses), [`registry`](#registry), [`kitRegistry`](#kitregistry), [`platforms`](#platforms), [`operator`](#operator).
45
+ Fields: [`name`](#name), [`secrets`](#secrets), [`git`](#git), [`harness`](#harness), [`sandbox`](#sandbox), [`priorityClasses`](#priorityclasses), [`callers`](#callers), [`cors`](#cors), [`hooks`](#hooks), [`mcp`](#mcp), [`runs`](#runs), [`registry`](#registry), [`kitRegistry`](#kitregistry), [`platforms`](#platforms), [`operator`](#operator).
46
46
 
47
47
  <a id="name"></a>
48
48
 
@@ -76,8 +76,9 @@ config loads: `<file> at secrets[<i>]<path>: <problem>`, followed by the shape o
76
76
  - Type: `string`
77
77
  - Required: yes
78
78
 
79
- As a Held secret, the name of the env var that the Harness container sees, which holds the
80
- Stand-in. As an Effect secret, the name that `secret("<name>")` names, and the name of the
79
+ As a Held secret, the name of its Stand-in: the file the Harness reads it from, the env var
80
+ that the engine reads a model key from, and the env var of the Agent's programs when the
81
+ `workspace()` lists it under `secrets`. As an Effect secret, the name that `secret("<name>")` names, and the name of the
81
82
  Effector's file. For a Held secret, it must match `[A-Z_][A-Z0-9_]*`:
82
83
  `secrets[<i>].name "<name>" is not an env var name ([A-Z_][A-Z0-9_]*)`. It must not start with
83
84
  `JR2_` and must not be a proxy or trust variable of the kit:
@@ -377,7 +378,7 @@ checks from inside the cluster that the endpoint answers.
377
378
 
378
379
  The API key, when the endpoint needs one. Read it from `process.env`. It is a Held secret,
379
380
  `JR2_PROVIDER_API_KEY`: `jr2 up` gives the value to the Custodian only, bound to the host of
380
- `baseUrl`, and the Harness container holds only its Stand-in. Thus `baseUrl` must be
381
+ `baseUrl`, and the Harness process holds only its secret Stand-in. Thus `baseUrl` must be
381
382
  `https://` when you set it:
382
383
  `harness.provider.apiKey is set, and harness.provider.baseUrl is not https://`.
383
384
 
@@ -467,6 +468,11 @@ deployments, so read it from `process.env` (`.env`).
467
468
 
468
469
  Env vars for the Harness container. The Agent can read every one of them, so do not put a
469
470
  credential here. A known model key is refused here. Put it in the top-level `secrets` list.
471
+ The Harness process gets each one, except a name that loads code from a path, such as
472
+ `NODE_OPTIONS` or `LD_PRELOAD`, and `HOME`, which is its own; the Agent's programs get each
473
+ one. A name that starts with `JR2_` is jr2's own, and is refused. A credential file that the
474
+ Harness process reads, such as `AWS_CONFIG_FILE`, is refused in a folder that the Agent's code
475
+ writes (`/work`, `/tmp`, `/home/jr2`), and gives a warning anywhere else.
470
476
  See `HarnessEnvVar`.
471
477
 
472
478
  <a id="harness-env-name"></a>
@@ -508,7 +514,10 @@ pod as it is. A reference to a Secret of the kit is refused:
508
514
  - Required: no
509
515
 
510
516
  Secrets and ConfigMaps that you manage, whose keys all become env vars of the Harness
511
- container. See `HarnessEnvFromSource`.
517
+ process and of the Agent's programs, as `env` does. In the container's spec each key has the
518
+ prefix `JR2_ENVFROM_`, so that the Harness can tell jr2's names from the image's; the Harness
519
+ removes it. A key that starts with `JR2_` is refused: `jr2 up` refuses it, and the Harness does
520
+ not start with one. See `HarnessEnvFromSource`.
512
521
 
513
522
  <a id="harness-envfrom-prefix"></a>
514
523
 
@@ -692,6 +701,239 @@ An existing PriorityClass for the Orchestrator, the operator and the Repo cache
692
701
 
693
702
  An existing PriorityClass for the Sandboxes and the Instance Harness.
694
703
 
704
+ <a id="callers"></a>
705
+
706
+ ## `callers`
707
+
708
+ - Type: `CallersConfig`
709
+ - Required: no
710
+ - Default: none; only the Instance token acts
711
+
712
+ The Callers that act on the Instance with a credential of their own, and what each may do.
713
+ See `CallersConfig`.
714
+
715
+ <a id="callers-tokens"></a>
716
+
717
+ ### `callers.tokens`
718
+
719
+ - Type: `Readonly<Record<string, CallerToken>>`
720
+ - Required: no
721
+
722
+ The named Caller tokens, by name. A name is lowercase letters, digits and `-`, at most 40
723
+ characters, from a letter to a letter or digit. The config load refuses `instance`, the
724
+ Instance token's name. A name that the config no longer declares stops working at the next
725
+ `jr2 up`.
726
+
727
+ <a id="callers-tokens-name-may"></a>
728
+
729
+ ### `callers.tokens.<name>.may`
730
+
731
+ - Type: `Scopes`
732
+ - Required: yes
733
+
734
+ What the token may do. See `Scopes`.
735
+
736
+ <a id="callers-tokens-name-may-start"></a>
737
+
738
+ ### `callers.tokens.<name>.may.start`
739
+
740
+ - Type: `ScopeGrant`
741
+ - Required: no
742
+
743
+ Start a run.
744
+
745
+ <a id="callers-tokens-name-may-answer"></a>
746
+
747
+ ### `callers.tokens.<name>.may.answer`
748
+
749
+ - Type: `AnswerGrant`
750
+ - Required: no
751
+
752
+ Answer a Gate, and read a run's open Gates without its context. A list item
753
+ `{ workflow, gates }` opens only the Gates that it names, by invoke id (see `GateGrant`).
754
+
755
+ <a id="callers-tokens-name-may-cancel"></a>
756
+
757
+ ### `callers.tokens.<name>.may.cancel`
758
+
759
+ - Type: `ScopeGrant`
760
+ - Required: no
761
+
762
+ Cancel a run.
763
+
764
+ <a id="callers-tokens-name-may-read"></a>
765
+
766
+ ### `callers.tokens.<name>.may.read`
767
+
768
+ - Type: `ScopeGrant`
769
+ - Required: no
770
+
771
+ Read a run's state: its status, its context and its feed.
772
+
773
+ <a id="callers-tokens-name-may-steer"></a>
774
+
775
+ ### `callers.tokens.<name>.may.steer`
776
+
777
+ - Type: `ScopeGrant`
778
+ - Required: no
779
+
780
+ Send a Steering message to an Agent of a run.
781
+
782
+ <a id="callers-issuers"></a>
783
+
784
+ ### `callers.issuers`
785
+
786
+ - Type: `readonly JwtIssuer[]`
787
+ - Required: no
788
+
789
+ The identity providers whose JWTs are Callers. See `JwtIssuer`. Two entries with one
790
+ `issuer` are refused.
791
+
792
+ <a id="callers-issuers-issuer"></a>
793
+
794
+ ### `callers.issuers[].issuer`
795
+
796
+ - Type: `string`
797
+ - Required: yes
798
+
799
+ The issuer's URL, exactly as the `iss` claim gives it, such as `https://dex.example.com`.
800
+
801
+ <a id="callers-issuers-audience"></a>
802
+
803
+ ### `callers.issuers[].audience`
804
+
805
+ - Type: `string`
806
+ - Required: yes
807
+
808
+ The audience that a JWT must name in its `aud` claim: for a login, the client id.
809
+
810
+ <a id="callers-issuers-jwks"></a>
811
+
812
+ ### `callers.issuers[].jwks`
813
+
814
+ - Type: `string`
815
+ - Required: no
816
+ - Default: the `jwks_uri` of the issuer's `/.well-known/openid-configuration`
817
+
818
+ The URL of the issuer's public keys (a JWKS).
819
+
820
+ <a id="callers-issuers-claims"></a>
821
+
822
+ ### `callers.issuers[].claims`
823
+
824
+ - Type: `object`
825
+ - Required: no
826
+ - Default: `{ name: "sub", shown: "email", groups: "groups" }`
827
+
828
+ The claims to read.
829
+
830
+ <a id="callers-issuers-claims-name"></a>
831
+
832
+ ### `callers.issuers[].claims.name`
833
+
834
+ - Type: `string`
835
+ - Required: no
836
+
837
+ The claim that is the Caller's name. Use a claim that the provider never reassigns.
838
+
839
+ <a id="callers-issuers-claims-shown"></a>
840
+
841
+ ### `callers.issuers[].claims.shown`
842
+
843
+ - Type: `string`
844
+ - Required: no
845
+
846
+ The claim that is the name that jr2 shows.
847
+
848
+ <a id="callers-issuers-claims-groups"></a>
849
+
850
+ ### `callers.issuers[].claims.groups`
851
+
852
+ - Type: `string`
853
+ - Required: no
854
+
855
+ The claim that holds the list of groups or roles that `groups` maps to scopes.
856
+
857
+ <a id="callers-issuers-groups"></a>
858
+
859
+ ### `callers.issuers[].groups`
860
+
861
+ - Type: `Readonly<Record<string, Scopes>>`
862
+ - Required: yes
863
+
864
+ The scopes that each value of the groups claim gives, such as
865
+ `{ "jr2-ops": { start: "all", answer: "all" } }`. A JWT with more than one value gets the scopes
866
+ of each, joined. A value that is not here gives nothing.
867
+
868
+ <a id="callers-issuers-login"></a>
869
+
870
+ ### `callers.issuers[].login`
871
+
872
+ - Type: `boolean`
873
+ - Required: no
874
+ - Default: `false`, because an issuer for machines (a CI provider's tokens) has no login page
875
+
876
+ The Console offers a login against this issuer ([ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md)): OIDC, the authorization code flow
877
+ with PKCE, as a public client, with `audience` as the client id. Register the client as public
878
+ (no secret), with the Console's root (`https://<console>/`) as its redirect URI. The Console
879
+ reads the issuer and the client id before the login, from the open route
880
+ `GET /console/config`: both are public values. A login needs the Console on https or on
881
+ localhost, and the issuer must let the Console's origin call its discovery document and its
882
+ token endpoint (CORS).
883
+
884
+ <a id="cors"></a>
885
+
886
+ ## `cors`
887
+
888
+ - Type: `readonly string[]`
889
+ - Required: no
890
+ - Default: none
891
+
892
+ The origins from which a browser app may call the Instance's HTTP API, such as
893
+ `https://ops.example.com`. Each entry is an origin alone: a scheme, a host and an optional port,
894
+ with no path. The Console is served from the Orchestrator's own origin, so it
895
+ needs no entry. The config load refuses an entry that is not an origin:
896
+ `<file> at cors[<i>]: <entry> is not an origin — scheme://host[:port], with no path`.
897
+
898
+ <a id="hooks"></a>
899
+
900
+ ## `hooks`
901
+
902
+ - Type: `Readonly<Record<string, Hook>>`
903
+ - Required: no
904
+ - Default: none
905
+
906
+ The Hooks of the Instance, by name: each one is a door, `POST /hooks/<name>`, for one outside
907
+ service. A Hook's value comes from its kind's constructor, which the config imports:
908
+ `github({ secret: secret("GH_HOOK") })`, `gitlab({ secret: secret("GL_HOOK"), may })`,
909
+ `discord({ publicKey: "…" })`. `may.answer` names the Workflows whose Gates the Hook may answer,
910
+ every Workflow by default. A Hook never starts a run. A name has the rule of a Caller token's
911
+ name. When the config loads, a bad entry is refused: `<file> at hooks.<name>: <problem>`.
912
+
913
+ <a id="mcp"></a>
914
+
915
+ ## `mcp`
916
+
917
+ - Type: `Readonly<Record<string, McpServer>>`
918
+ - Required: no
919
+ - Default: none
920
+
921
+ The MCP servers, by a name of yours: `[a-z][a-z0-9-]*`, not `jr2`. A remote server has a
922
+ `url` (`https://` with a secret), a `secret` (a name of the `secrets` list; with no `hosts`, it
923
+ is bound to the url's host) and a `timeout` (default `"60s"`). A stdio server has a `command`
924
+ of the Sandbox Image, its `args` and a `timeout`. An Agent names a server in its `mcp` list.
925
+ See [`McpServer`](authoring-surface/type-mcpserver.md).
926
+
927
+ <a id="runs"></a>
928
+
929
+ ## `runs`
930
+
931
+ - Type: [`RunsConfig`](authoring-surface/type-runsconfig.md)
932
+ - Required: no
933
+ - Default: `{ retain: "30d", volume: "5Gi" }`
934
+
935
+ How long ended runs are kept, and the volume size.
936
+
695
937
  <a id="registry"></a>
696
938
 
697
939
  ## `registry`
@@ -34,7 +34,7 @@ Sandbox will run must be in the spec at spin-up.
34
34
 
35
35
  One Harness **image** serves many **Agents** — the Harness resolves model, instructions, and its Menu at runtime without
36
36
  a rebuild, reading the definition each admission carries (ADR-0018, ADR-0049) and reading the Menu through the Custodian
37
- per turn (ADR-0013). So the Orchestrator injects a fixed Harness image plus **config** (env + the prepared worktree),
37
+ per turn (ADR-0013). So the Orchestrator injects a fixed Harness image plus **config** (env + the prepared clones),
38
38
  never a per-Agent image build. The HTTP prompt body itself carries only `{message, images}`, so anything persona-shaping
39
39
  is set at provision time, not per request.
40
40
 
@@ -91,14 +91,15 @@ one keeps the name and the endpoint and costs no node anything. Deferred until a
91
91
  These are accepted gaps in the current operator, recorded so they aren't silently forgotten. Each is a deliberate
92
92
  deferral.
93
93
 
94
- - **Egress is not restricted.** The pod is hardened at the host boundary — `automountServiceAccountToken: false`,
95
- pod/container `securityContext` (`runAsNonRoot`, `allowPrivilegeEscalation: false`, drop `ALL`, seccomp
96
- `RuntimeDefault`) — and its _ingress_ is the Orchestrator's alone: `jr2 up` converges a NetworkPolicy that admits no
97
- other pod to a Sandbox ([ADR-0058](0058-a-harness-answers-the-orchestrator-alone.md)), which kind's `kindnet`
98
- enforces. Nothing yet restricts _egress_: an untrusted Agent can still reach the cluster's API server IP (it holds no
99
- credential there) and the internet. A default-deny egress policy with an allowlist is the next layer, and ADR-0058
100
- records it open. The ADR-0013 boundary does not depend on either: only the token bounds what a pod may _do_;
101
- NetworkPolicy bounds where it may _talk_.
94
+ - **Egress is open.** The pod is hardened at the host boundary — `automountServiceAccountToken: false`, pod/container
95
+ `securityContext` (`runAsNonRoot`, `allowPrivilegeEscalation: false`, drop `ALL`, seccomp `RuntimeDefault`) — and its
96
+ _ingress_ is the Orchestrator's alone: `jr2 up` converges a NetworkPolicy that admits no other pod to a Sandbox
97
+ ([ADR-0058](0058-a-harness-answers-the-orchestrator-alone.md)), which kind's `kindnet` enforces. _Egress_ is open
98
+ except for the cloud metadata addresses, which no Harness pod reaches
99
+ ([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)): an untrusted Agent can
100
+ still reach the cluster's API server IP (it holds no credential there) and the internet. A default-deny egress policy
101
+ with an allowlist is the next layer, and ADR-0058 records it open. The ADR-0013 boundary does not depend on either:
102
+ only the token bounds what a pod may _do_; NetworkPolicy bounds where it may _talk_.
102
103
  - **Sidecars are plain containers, not native sidecars.** They are scheduled as ordinary `containers`, not Kubernetes
103
104
  ≥1.29 native sidecars (`initContainers` with `restartPolicy: Always`). There is no start-ordering or
104
105
  termination-ordering guarantee between the Harness and its sidecars — the Harness may begin serving before the
@@ -16,21 +16,88 @@ action observably occurred only post-approval.
16
16
 
17
17
  The split-channel model:
18
18
 
19
- | Direction | Kind | Mechanism |
20
- | --------- | --------------------------------------------------------- | ---------------------------------------- |
21
- | up | control / decision (the workflow's events, ADR-0011) | Menu tool call → Custodian → delivery |
22
- | up | progress / telemetry (tokens, attempts, "still working") | Harness stream (lossy, observation-only) |
23
- | down | the next turn's prompt (feedback, steering at a boundary) | a new submission on the same iid |
24
- | down | the Turn part, restated (the Allowed picks changed) | the Harness's read at each step boundary |
25
- | down | interrupts (deliberate terminal cancel) | `abort` on the wire |
26
-
27
- Tool calls are Agent-initiated, so the Machine speaks inside a Turn only as an answer. The one exception is the
28
- **restated Turn part**: when something other than the Agent's own pick changes its Allowed picks — a Gate's answer, an
29
- outside event — the Harness learns it by reading the surface at each step boundary and appends the new list
19
+ | Direction | Kind | Mechanism |
20
+ | --------- | -------------------------------------------------------------- | --------------------------------------------- |
21
+ | up | control / decision (the workflow's events, ADR-0011) | Menu tool call → Custodian → delivery |
22
+ | up | progress / telemetry (tokens, attempts, "still working") | Harness stream (lossy, observation-only) |
23
+ | down | the next turn's prompt (feedback, steering at a boundary) | a new submission on the same iid |
24
+ | down | the Turn part, restated (the Allowed picks changed) | the Harness's read at each step boundary |
25
+ | down | a jr2 notice (a memory kill, ADR-0062), typed, to its audience | the same step-boundary read, per conversation |
26
+ | down | a Steering message (a Caller's or the Machine's free text) | the same step-boundary read, per Agent slot |
27
+ | down | interrupts (deliberate terminal cancel) | `abort` on the wire |
28
+
29
+ Tool calls are Agent-initiated, so the Machine answers a pick only when the Agent makes one. Inside a Turn, three other
30
+ things reach the Agent, and each one at a step boundary. The first is the **restated Turn part**: when something other
31
+ than the Agent's own pick changes its Allowed picks — a Gate's answer, an outside event — the Harness learns it by
32
+ reading the surface at each step boundary and appends the new list
30
33
  ([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)). It is a pull, not a push:
31
- nothing new reaches the Harness, and what is restated is fixed text plus Menu names. The residual hard case is
32
- **unsolicited mid-turn steering** — free text from the Machine to an Agent that has not asked. That remains unsolved,
33
- and it is the only behavior in that bucket, not a dependency of the whole control plane.
34
+ the Harness asks at a step boundary, and nothing reaches it that it did not ask for. What is restated is fixed text plus
35
+ Menu names. The second is jr2's own **typed notices**, which ride that same step-boundary read: the read of an open Turn
36
+ returns its conversation's pending notices, the Harness appends them, and its acknowledgment of the append delivers
37
+ them. So a running Agent hears a fact the mechanism knows — a memory kill above all — without an interrupt. A notice is
38
+ typed data, and the Harness words it ([ADR-0062](0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md)). The third
39
+ is a **Steering message**: free text to an Agent that has not asked for it, on the same read (below). Each is a pull at
40
+ a step boundary, never an interrupt.
41
+
42
+ ## Steering messages
43
+
44
+ - **Two senders.** A Caller ([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md))
45
+ sends one with `POST /runs/:runId/steering { agent, text }` or `jr2 steer <run> <agent> "<text>"`, and needs the
46
+ `steer` scope for the run's Workflow. A text above 16 KiB of UTF-8 is 413, and an empty one 400. The route answers 202
47
+ `{ id, agent, open }`: `open` says whether a Turn of the slot was open when the message came. The message names the
48
+ Caller by the name that jr2 shows (a JWT's `email`, else its name), with its `via` and its `onBehalfOf` claim. The
49
+ Machine sends one with the authoring action `steer(<agent>, text, { from? })` in a transition, to an Agent of the
50
+ Machine that runs the action; `from` is the Sender whose words it carries. The action cuts a text above 16 KiB and
51
+ says so in the text, because a relayed payload is not the author's to bound, and a refusal there would fail the
52
+ transition.
53
+ - **Addressed by Agent slot.** `agent` names an Agent slot of the run, as the run's slot list names it. The list,
54
+ `{ agent, open, steerable }` for each slot at `GET /runs/:runId/agents`, is open to a Caller with `steer`, so a Caller
55
+ can see where a message would go. It lists each Agent that each live Machine instance carries in its `actors`, with or
56
+ without an open Turn, and `jr2 steer <run>` prints it. An Instance ID is not the address: jr2 computes it, a Caller
57
+ cannot know it, and a fresh Turn gets a new one. A slot is the name that the author gave, and the walk lets at most
58
+ one state of a Machine instance hold it at a time ([ADR-0015](0015-authoring-surface-absorbs-the-mechanism.md)), so it
59
+ names one conversation at a time.
60
+ - **Delivery.** When the slot has an open Turn, its Harness appends the message at the Turn's next step boundary, on the
61
+ same read as a notice. A pick that ends the Turn skips that read. Thus a message that comes while the Turn's last
62
+ model request runs is held for the slot's next Turn, as when no Turn is open, and `jr2 steer` says that both can
63
+ occur. Otherwise the message is held for the slot's next Turn of the same Machine instance, and that Turn's admission
64
+ carries it. A held message belongs to the Machine instance that was live when it was sent: when that instance ends,
65
+ the message retires, and the run records it "not delivered". A Machine instance ends when it leaves its parent's
66
+ children, when it reaches `done` or `error`, and when the run ends; a host that stops parks the run, and ends nothing.
67
+ An instance below one that ended ends with it. When no live Machine instance holds the slot — it has ended, or it
68
+ never was — the route answers 409 with the run's slots, because the Orchestrator cannot tell a slot that ended from
69
+ one that never was; an unknown run is 404, and a run that is not live is 409. The Agent hears each message once, by
70
+ the rules of a notice (ADR-0062). The run records each message with its author, its text, when it was sent and its
71
+ outcome (`held`, `heard`, `not-delivered`), in the run's persisted state; the record keeps the newest 100 and each one
72
+ still held. The run's status carries it as `steering`, behind `read` like `context` (ADR-0014): a message is a
73
+ Caller's working text, never the open band's.
74
+ - **Never a Machine event.** A Steering message is in no Vocabulary, no Gate takes it, and no guard reads it. The Agent
75
+ decides what to do with it, and acts on the workflow only by its picks.
76
+ - **Agents message each other only through the Machine.** An Agent's Menu offers a pick that the author declared; the
77
+ Machine's transition relays its payload with `steer(<agent>, …)`. jr2 gives no built-in message tool.
78
+ - **The Harness frames the message by its author** (ADR-0062). A Caller's message is an instruction from outside the
79
+ run, with the Caller's name. A message that the Machine relays from another Agent is information from a peer, and the
80
+ frame says that it is not an instruction. A Machine's message that relays no one is the workflow's own text.
81
+ - **`steerable: false` on an Agent definition refuses Callers only** (403). The Machine can still steer that Agent: the
82
+ author wrote both. The admission strips it from the definition, because the model never reads it.
83
+ - **What a model reads changes, so two `@model` claims are owed**
84
+ ([ADR-0066](0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md)): `steer-follow`, an
85
+ Agent follows a Caller's Steering message that comes in its open Turn, and `peer-not-order`, an Agent does not obey a
86
+ peer's relayed word as an order. Their floors were set before a measured run, as `chart-refusal`'s was: `steer-follow`
87
+ 9 of 10, a list read last's floor, because the message is the newest text that the model read; and `peer-not-order` 7
88
+ of 10, because there the peer's word comes after the workflow's text, and ADR-0029 measured how much the text read
89
+ last pulls.
90
+
91
+ Considered and rejected: address a message by conversation (an Instance ID), which a Caller cannot know and which
92
+ changes at a fresh Turn; a built-in tool with which an Agent messages another Agent, which goes around the Machine that
93
+ the author wrote; and a Steering message as a Machine event, which makes free text control.
94
+
95
+ Considered and not chosen now: a held message turns back a pick that ends the Turn. The Harness would refuse that pick
96
+ while a message waits for the slot, and the model would read the message and pick again, so a message always reaches the
97
+ Turn that was open when it came. It costs a new refusal that the model reads (an `@model` claim), one more model request
98
+ in that case, and a pick that a Caller's message can undo, where today a pick is final. The case is one model request
99
+ wide, and a Gate or the Machine can still send the Agent back. Look at it again if Callers need a message to reach the
100
+ Turn that was open.
34
101
 
35
102
  ## Durability: the admission is the handle
36
103
 
@@ -91,34 +91,40 @@ fast setup.
91
91
 
92
92
  ## Where setup runs
93
93
 
94
- Worktree provisioning is a post-`Ready` step owned by the Orchestrator: the Sandbox CRD names repositories by identity
95
- so the operator can place and mount them, but it knows nothing about clones or worktrees (ADR-0001, ADR-0051).
96
- `workspace()`'s attach is one idempotent in-pod script the Harness runs on `POST /attach` (ADR-0012,
97
- [ADR-0063](0063-the-orchestrator-watches-the-cluster.md)), once `Ready` says every cache is present and fetched.
94
+ The clone is a post-`Ready` step owned by the Orchestrator: the Sandbox CRD names repositories by identity so the
95
+ operator can place and mount them, but it knows nothing about clones or worktrees (ADR-0001, ADR-0051). `workspace()`'s
96
+ attach is one idempotent in-pod script the Harness runs on `POST /attach` (ADR-0012,
97
+ [ADR-0063](0063-the-orchestrator-watches-the-cluster.md)), once `Ready` says every cache is present and fetched. The
98
+ attach only clones. A Machine cuts each Worktree with the kit Effect `worktree()`
99
+ ([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)).
98
100
 
99
101
  ## In-Sandbox layout: worktrees off the per-Sandbox clone (gwtmux convention)
100
102
 
101
103
  "Not a shared worktree" above means _not a worktree off a fleet-shared `.git`_. **Inside** a Sandbox, worktrees are the
102
- intended ergonomic — taken off the Sandbox's **own** clone. The per-Sandbox clone _is_ the `default/`, and branch
103
- working trees are its siblings, matching the layout [gwtmux](https://github.com/snapwich/gwtmux) already expects, under
104
- the slot the Machine gave the repository (ADR-0051):
104
+ intended ergonomic — taken off the Sandbox's **own** clone. The per-Sandbox clone _is_ the `default/`, and the Worktrees
105
+ are its siblings, matching the layout [gwtmux](https://github.com/snapwich/gwtmux) already expects, under the slot the
106
+ Machine gave the repository (ADR-0051):
105
107
 
106
108
  ```
107
- /work/<slot>/default/ the per-Sandbox `git clone --shared` (holds the pod-local .git; the default branch checked out)
108
- /work/<slot>/<branch>/ `git worktree add` siblings, one per branch worked
109
+ /work/<slot>/default/ the per-Sandbox `git clone --shared` (holds the pod-local .git; stays on the Binding's base)
110
+ /work/<slot>/<name>/ `git worktree add` siblings: folders a Machine cuts, each with one branch or one commit
109
111
  ```
110
112
 
111
113
  Because the layout is identical to the one used outside the cluster, existing worktree tooling works **unchanged** when
112
- you exec into a Sandbox. Object resolution falls through: a branch worktree shares the per-Sandbox `default/.git`, whose
114
+ you exec into a Sandbox. Object resolution falls through: a Worktree shares the per-Sandbox `default/.git`, whose
113
115
  alternates point at the read-only cache — existing objects are read from the cache, new commits land in the pod-local
114
- `.git`. `default/` is a checkout of the Repo's default branch, as gwtmux's `default/` is outside the cluster: work
115
- happens in the branch worktrees, and `default/` is the reference tree a human or a tool finds where it expects one. Not
116
- `--no-checkout`: an empty working tree reads as every tracked file deleted to `git status`, which misleads an Agent that
117
- looks. The branch name `default` is refused by the spec guard: the branch Worktree is a sibling of that directory, never
118
- it. `origin`'s push url is the binding's own spelling, so a Machine that bound over ssh pushes over ssh even when the
119
- cache was cloned over https.
120
-
121
- This does **not** reintroduce jr's contention: the shared `.git` here is _per-Sandbox_, and it is accessed serially
122
- under the standing invariant that **one Agent runs at a time per Workspace** — structural since ADR-0012 (one sequential
123
- body per workspace). A Machine that runs concurrent Agents in one Sandbox, or an Agent that spawns parallel git-touching
124
- subagents, reintroduces contention (pod-scoped, not fleet-wide) and must opt into separate Sandboxes instead.
116
+ `.git`. `default/` stays on the Binding's base: a branch is attached, a tag or a commit is detached, and with no base it
117
+ is the Repo's default branch, as gwtmux's `default/` is outside the cluster. No work happens in it: work happens in the
118
+ Worktrees, and `default/` is the reference tree that a human, a tool or an Agent that only reads finds where it expects
119
+ one. Not `--no-checkout`: an empty working tree reads as every tracked file deleted to `git status`, which misleads an
120
+ Agent that looks. A Worktree's folder cannot be named `default`, and a cut of the branch that `default/` holds is
121
+ `refused`, with a reason that names `/work/<slot>/default`. `origin`'s push url is the binding's own spelling, so a
122
+ Machine that bound over ssh pushes over ssh even when the cache was cloned over https.
123
+
124
+ This does **not** reintroduce jr's contention: the shared `.git` here is _per-Sandbox_, so what contends stays in one
125
+ pod and never crosses the fleet. Inside the pod it is the author's choice. A Machine that runs concurrent Agents in
126
+ Worktrees of one clone, or an Agent that spawns parallel git-touching subagents, takes that contention, and a Machine
127
+ that does not want it uses separate Sandboxes. Two things keep one clone safe to share. The attach sets `gc.auto=0` and
128
+ `maintenance.auto=false` in the clone, because a `gc` beside a cut or a commit can prune what another Worktree needs.
129
+ And all Worktrees of a clone share `refs/remotes/origin/*`, so `jr2-upload-pack` holds a per-clone lock for each fetch
130
+ (ADR-0053, ADR-0075).