@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
@@ -2,26 +2,32 @@
2
2
 
3
3
  An Effect is work a Machine does outside itself. A Machine already runs Agents and Gates as actors (ADR-0049); work such
4
4
  as running the tests had no place of its own, so an author either asked an Agent to do it (and trusted its word) or
5
- wrote a raw xstate promise that a restart ran again. jr2 builds four kinds: `command`, run a program in the Workspace;
6
- `http`, call a URL — with a JSON or text body, or a Worktree file as its body; `push`, push the Workspace's branch to
7
- its Repo Slot's Binding; and `dispatch`, start a run of a registered Workflow, on this Orchestrator or another's
8
- ([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)). An Effect that holds a secret runs in an Effector
9
- ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)). A `command` whose env names a secret —
10
- the credentialed `command` — runs there too, its child confined (below). A `dispatch` with no target runs in the
11
- Orchestrator's own process, the one Effect that does: it starts the run through the run host and holds no secret.
5
+ wrote a raw xstate promise that a restart ran again. jr2 builds five kinds: `command`, run a program in the Workspace;
6
+ `http`, call a URL — with a JSON or text body, or a Worktree file as its body; `push`, push a Worktree's branch to its
7
+ Repo Slot's Binding; `dispatch`, start a run of a registered Workflow, on this Orchestrator or another's
8
+ ([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)); and `mcp`, call a tool of an MCP server, or read its
9
+ resource or get its prompt
10
+ ([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)). Beside
11
+ them, `worktree()` is a kit Effect on the `command` machinery that cuts a Worktree
12
+ ([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)). An Effect that holds a secret runs in an
13
+ Effector ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)). A `command` whose env names a
14
+ secret — the credentialed `command` — runs there too, its child confined (below). A `dispatch` with no target runs in
15
+ the Orchestrator's own process, the one Effect that does: it starts the run through the run host and holds no secret.
12
16
 
13
17
  ## Decision
14
18
 
15
19
  - **An Effect is an actor slot beside `agent()`.** `actors: { tests: command(["pnpm", "test"]) }`, invoked as
16
20
  `src: "tests"`, with an `onDone` that routes on its output. xstate gives the rest: the Effect stops when its state
17
21
  exits, its input persists with the snapshot, a `src` typo is a compile error, and `.provide()` is the unit-test seam.
18
- The slot carries its parts as plain data — kind, placement, timeout; a `command`'s argv, Repo Slot and the secrets its
19
- env names; an `http` Effect's method, fixed host, the names of the secrets it names and whether its body is a Worktree
20
- file; a `push`'s `pin`, `sign`, `squash` and `force` — which `jr2 up` prints and the Machine doc carries, read without
21
- running anything. A packaged Machine may leave an `http` url (whatever its body) or a `command`'s argv Open (`open` in
22
- its place), and the name of one secret its auth, a header or a command's env names (`secret(open)`; never in a url),
23
- for the composer to bind once with `customize(machine, { effects: { <slot>: { url } | { argv } | { secret } } })` —
24
- one object with both keys for an Effect with two Open parts
22
+ The slot carries its parts as plain data — kind, placement, timeout; a `command`'s argv and the secrets its env names;
23
+ an `http` Effect's method, fixed host, the names of the secrets it names and whether its body is a Worktree file; a
24
+ `push`'s `pin`, `sign`, `squash`, `force` and `slot`; an `mcp` Effect's operation, server and tool; a cut's `expect` —
25
+ which `jr2 up` prints and the Machine doc carries, read without running anything. A packaged Machine may leave an
26
+ `http` url (whatever its body), a `command`'s argv, or an `mcp` Effect's server or tool Open (`open` in its place),
27
+ and the name of one secret its auth, a header or a command's env names (`secret(open)`; never in a url), for the
28
+ composer to bind once with
29
+ `customize(machine, { effects: { <slot>: { url } | { argv } | { secret } | { server } | { tool } } })` — one object
30
+ with both keys for an Effect with two Open parts
25
31
  ([ADR-0054](0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)); the slot then carries the
26
32
  Effect built again with the bound parts, and an Effect still Open runs nothing: its state's `onError`. An Effect
27
33
  written inline (`invoke: { src: http({...}) }`) is refused at build and at load, with the slot form in the message:
@@ -51,7 +57,7 @@ Orchestrator's own process, the one Effect that does: it starts the run through
51
57
  is written below (on a state's entry or exit, or on a transition of a state, at or below that Agent's state). On a
52
58
  transition below no Agent's state it shapes the pick of the Agent whose event took the transition — the sender of a
53
59
  pick, or the Agent whose pick started the child whose done or error event it is — and the other Agent's pick keeps its
54
- default answer; when one Agent slot is invoked in two regions, it shapes the pick at the same invoke as the pick that
60
+ default answer; when one Agent slot is invoked by two states, it shapes the pick at the same invoke as the pick that
55
61
  sent the event or started the child, and none when that pick is not being answered; an event that names no Agent
56
62
  shapes the pick in its own send, and outside one shapes no pick while two Agents' states are active; on an entry or an
57
63
  exit below no Agent's state it sets nothing. One action object written in several places runs from each by that
@@ -77,27 +83,40 @@ Orchestrator's own process, the one Effect that does: it starts the run through
77
83
  `http` whose body is a Worktree file, are placed in that Sandbox Effector — the command runs there with its child
78
84
  confined (below). A `dispatch` with no target runs in the Orchestrator's own process, the fourth Placement,
79
85
  `orchestrator`, and the one Effect that has it; with a target it runs in the Instance Effector
80
- ([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)). There is no `placement:` key and no kind per
81
- placement: naming a secret places a command, the body places an `http`, and the target places a `dispatch` — one name
82
- per thing, so there is no `upload` kind beside it (ADR-0068).
83
- - **A `command` that names no secret runs in the Harness container of its Workspace's Sandbox**, as a child of the
84
- Harness process: the Agent's user, the Sandbox Image's tools, the same Stand-ins as the Agent's `bash` (ADR-0059), and
85
- no credential of its own. The Harness serves it on `/exec/:id`, behind the same bearer as every Harness route
86
- (ADR-0058); the Instance Harness holds no Workspace and refuses it (ADR-0031). Outside a `workspace()` a command is
87
- refused at invoke. Its cwd is a Repo Slot's Worktree: the one slot, or the slot it names (`slot`); several and none
88
- named is refused (ADR-0051). In a Workspace with no Repo Slot its cwd is `/work`, which the Workspace's steps share —
89
- named in the start, never left to the Harness, whose own cwd is the Sandbox Image's `WORKDIR`.
86
+ ([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)). An `mcp` Effect runs in the Instance Effector,
87
+ because in v1 it calls a remote server only. There is no `placement:` key and no kind per placement: naming a secret
88
+ places a command, the body places an `http`, and the target places a `dispatch` — one name per thing, so there is no
89
+ `upload` kind beside it (ADR-0068).
90
+ - **A `command` that names no secret runs in the Harness container of its Workspace's Sandbox**, as a confined child of
91
+ the Harness process ([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)): the
92
+ Agent's user, the Sandbox Image's tools, the same confined env and Stand-ins as the Agent's `bash` (ADR-0059), and no
93
+ credential of its own. The Harness serves it on `/exec/:id`, behind the same bearer as every Harness route (ADR-0058);
94
+ the Instance Harness holds no Workspace and refuses it (ADR-0031). Outside a `workspace()` a command is refused at
95
+ invoke. Its cwd is the Worktree its input names (`worktree`), which is required under Repo Slots, because a Workspace
96
+ that only clones has no Worktree for a default to name (ADR-0075). In a Workspace with no Repo Slot its cwd is
97
+ `/work`, which the Workspace's steps share — named in the start, never left to the Harness, whose own cwd is the
98
+ Sandbox Image's `WORKDIR`.
99
+ - **`worktree()` runs on the `command` machinery.** `actors: { cut: worktree({ expect? }) }`, with the input
100
+ `{ path, branch, from? }` or `{ path, detach }`: a kit program in the Harness container, its git confined (ADR-0076),
101
+ no new wire kind, marked idempotent. Its outcomes are `ready | refused | unknown`, and its output is the contract in
102
+ ADR-0075. Each thing that an Agent's word can cause settles `refused`, with nothing changed, and never goes to
103
+ `onError`. A settled `ready` row in the ledger holds the cut's Repo Slot, path and branch, and it is the one thing a
104
+ `push`, a credentialed `command` and an `http` file body read for their target.
90
105
  - **Every outcome is output**: `exited | timeout | killed | unknown` for a `command` (`killed` with the signal that
91
106
  ended it; a stopped credentialed command's is `exited` with the stop's signal, D1);
92
107
  `response | unreachable | refused | unknown` for an `http` Effect; `pushed | rejected | diverged | unknown` for a
93
- `push`. A command not found is `exited` 127. A start over the bytes an Effector reads (ADR-0068) is sent nowhere and
94
- settles as its kind's own outcome — an `http` `refused`, a `push` `rejected` at stage `start` (nothing pushed), a
95
- credentialed `command` `exited` 127 with the reason in its tail, the Effector's own form for a command that could not
96
- start — since an Agent's param (a squash message, an argv part) can reach the bound; a path segment that is `.`, `..`
97
- or empty, and a file body's path that is a symlink, leaves the Worktree, is absolute, is a FIFO or has more than one
98
- link, are `refused` with nothing sent — an Agent's param can cause each. `onError` is only a jr2 or config defect: no
99
- Workspace, an argv slot with no value, an input that misses the definition's schema, no Effector to send to, an Open
100
- part nobody bound.
108
+ `push`; `returned | refused | unreachable | input-required | unknown` for an `mcp` Effect. A command not found is
109
+ `exited` 127, and a bad Worktree adds no outcome: a `command` settles `exited` 127 with the reason in its tail, and an
110
+ `http` file body settles `refused`. Every union here is open: `worktree()`, `push`, `command` and `http` each hold a
111
+ stand-in member, so that no author can write an exhaustive check and a later outcome is no break; the `mcp` union
112
+ holds one too. A start over the bytes an Effector reads (ADR-0068) is sent nowhere and settles as its kind's own
113
+ outcome — an `http` `refused`, a `push` `rejected` at stage `start` (nothing pushed), a credentialed `command`
114
+ `exited` 127 with the reason in its tail, the Effector's own form for a command that could not start — since an
115
+ Agent's param (a squash message, an argv part) can reach the bound; a path segment that is `.`, `..` or empty, and a
116
+ file body's path that is a symlink, leaves the Worktree, is absolute, is a FIFO or has more than one link, are
117
+ `refused` with nothing sent — an Agent's param can cause each. `onError` is only a jr2 or config defect: no Workspace,
118
+ a `command` with no `worktree` under Repo Slots, an argv slot with no value, an input that misses the definition's
119
+ schema, no Effector to send to, an Open part nobody bound.
101
120
  - **An `http` Effect is a definition plus one function.**
102
121
  `http({ method, url, headers?, auth?, input?, request?, body?, timeout?, maxBody?, idempotent? })`. The url's scheme
103
122
  and host are literal — a secret, named with `secret("name")`, may sit only in its path, through the `text` template,
@@ -108,19 +127,53 @@ Orchestrator's own process, the one Effect that does: it starts the run through
108
127
  the url is refused. `request(input)` returns the run's part alone: `path` segments and a JSON or text body. So an
109
128
  Agent's param reaches a path segment or a body, never the host, a header or a secret. It runs in the Instance
110
129
  Effector, which builds the request and holds the secrets (ADR-0068); the Orchestrator signs the call and holds none.
111
- Its body may instead be a Worktree file, `body: file(path, { slot?, segments?, contentType? })`: the file's path
112
- relative to the Repo Slot's Worktree (the one slot, or the one `slot` names) — or to `/work` itself in a Workspace
113
- with no Repo Slot (ADR-0051), the folder its steps share — and the url's path segments — each a value or a function of
114
- the input; no `request` is written beside it, since `file()` carries the run's part. Such an `http` is placed in the
115
- Sandbox Effector of the Workspace whose body invokes it, and `jr2 up` refuses one that no `workspace()` invokes, where
116
- no Worktree exists to read. It is sent with `PUT`, `POST` or `PATCH`. The Sandbox Effector opens the file with
117
- `openat2` beneath the work root — `RESOLVE_BENEATH`, `RESOLVE_NO_SYMLINKS`, `RESOLVE_NO_MAGICLINKS`,
130
+ Its body may instead be a Worktree file, `body: file(path, { segments?, contentType? })`: the file's path relative to
131
+ the Worktree its input names (`worktree`), which needs a cut's settled row, as a `push` does — or to `/work` itself in
132
+ a Workspace with no Repo Slot (ADR-0051), the folder its steps share — and the url's path segments — each a value or a
133
+ function of the input; no `request` is written beside it, since `file()` carries the run's part. Such an `http` is
134
+ placed in the Sandbox Effector of the Workspace whose body invokes it, and `jr2 up` refuses one that no `workspace()`
135
+ invokes, where no Worktree exists to read. It is sent with `PUT`, `POST` or `PATCH`. The Sandbox Effector opens the
136
+ file with `openat2` beneath the work root — `RESOLVE_BENEATH`, `RESOLVE_NO_SYMLINKS`, `RESOLVE_NO_MAGICLINKS`,
118
137
  `RESOLVE_NO_XDEV`, non-blocking — then `fstat`: a symlink at any component (one that stays inside included), `..`, an
119
138
  absolute path, a FIFO, a directory and a file with more than one link are refused, in words, as output. The content
120
139
  type is `file()`'s `contentType` or a `content-type` header of the definition's, not both: a definition with both is
121
140
  refused when the Machine loads ("write one of them", as for `auth` beside an `authorization` header). With neither it
122
141
  is `application/octet-stream`. A later body form (a multipart file and fields) is one more body on `http`, never a
123
142
  kind.
143
+ - **An `mcp` Effect has the shape of `http`, with a server and a tool in place of a url.**
144
+ `mcp.call(server, tool, { input?, timeout?, maxBody?, idempotent? })`,
145
+ `mcp.read(server, uri, { timeout?, maxBody?, idempotent? })` and
146
+ `mcp.prompt(server, name, { input?, timeout?, maxBody?, idempotent? })`
147
+ ([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)).
148
+ `server` is a name of the `mcp` block of `jr2.config.ts`, so the server's url and its secret are the config's and
149
+ never a param; the secret is the Effect's Effect secret, placed in the Instance Effector (ADR-0068). `server` can be
150
+ Open for each of the three; `tool` for `mcp.call`; a read's `uri` and a prompt's `name` are the author's. `jr2 up`
151
+ refuses an `mcp` Effect whose server the `mcp` block does not declare, or whose server is a stdio server, and prints
152
+ the `mcp` block entry that declares a remote server, and the `customize()` line for an Open server. `input` is the
153
+ schema of the tool's arguments (a prompt's arguments are strings), and the invoke's input is the arguments, which
154
+ `fromPick` fills from a pick's params: an Agent's param reaches the arguments only. An Effect's `timeout` replaces its
155
+ server's `timeout` for that Effect; with none, the server's applies (60 seconds by default). The outcomes:
156
+ `returned { isError, text, structured?, truncated? }` — the server answered the call, `isError` being the tool's own,
157
+ `structured` its `structuredContent`, typed `unknown`, and `truncated` set when the Effector cut `text` or dropped
158
+ `structured` at the bound (ADR-0068), and a reason keeps at most 1 KiB of a server's error message; `refused` — the
159
+ request did not run: the server answered a 3xx or a 4xx status (a 401 or a 403 included) at any step, the handshake
160
+ too, refused the handshake, or answered the request with the JSON-RPC error `-32700`, `-32600`, `-32601` or `-32602`
161
+ (a request it could not read, an unknown method, an unknown tool, bad arguments), or answered a read with `-32002`
162
+ (the resource does not exist); or the Effector sent nothing because the server or its secret is not in its
163
+ `effector.json`, the second fence behind the `jr2 up` refusal; `unreachable` — nothing of the request was sent: the
164
+ name did not resolve, or the connection, TLS or the handshake failed otherwise (a 5xx, the timeout, an answer that is
165
+ not MCP's, a handshake that names an MCP revision the client does not speak); `input-required` — the server asked for
166
+ input, which jr2 does not give, so the Machine routes it: the error `-32042` (URL elicitation required), after which
167
+ the tool did not run, or an `elicitation/create` request, which the client declared no capability for and which comes
168
+ while the tool runs, so the tool may have begun; jr2 does not send the request again, and a Machine that invokes the
169
+ Effect again after a Gate can run a part of the tool two times; `unknown` — the request was sent and no answer came
170
+ (the timeout, a reset, a stream that ended first and that the server did not give again from its last event id), or
171
+ the server answered with a 5xx or with another JSON-RPC error, after which the tool may have acted. `idempotent` is
172
+ the author's mark alone; the server's `idempotentHint` is ignored. `mcp.read` and `mcp.prompt` settle the same
173
+ outcomes, and their `returned` is `{ text, structured?, truncated? }`: for a read, `text` joins the text of the
174
+ resource's contents and `structured` is the `contents` list, with a blob given only as its `mimeType` and size; for a
175
+ prompt, `text` joins the text of the prompt's messages and `structured` is the `messages` list. An `output:` schema
176
+ for `structured` is Later.
124
177
  - **A credentialed `command`: `command(argv, { env: { NAME: secret("x") | secret("x").path }, pin? })`.** Naming a
125
178
  secret is what places it: it runs in the Sandbox Effector of the Workspace whose body invokes it, with no `placement:`
126
179
  key — and so does an env that names `secret(open)`, whose name the composer binds (ADR-0054). Its child runs as the
@@ -129,7 +182,9 @@ Orchestrator's own process, the one Effect that does: it starts the run through
129
182
  run the image's system paths, read and write its own scratch (its `HOME` and its `TMPDIR`; never `/tmp`, which every
130
183
  child would share), and read the secret files it names — nothing else. A node whose kernel cannot confine it runs
131
184
  none: the Effector of a Workspace that holds one does not become ready there, and the provision fails naming the node
132
- (ADR-0068). It runs against one commit: the Agent's branch tip when it starts, or — with `pin` — the commit the
185
+ (ADR-0068). Its input names its Worktree (`worktree`), and that path needs a cut's settled row with a branch, as a
186
+ `push` does: a path alone does not name the target of a credential. With none it settles `exited` 127, and nothing
187
+ runs. It runs against one commit: the tip of that row's branch when it starts, or — with `pin` — the commit the
133
188
  Effect's input names (`{ sha }`, one a Gate approved). That commit is checked out into a fresh scratch of the Effect's
134
189
  own by the push's import procedure (below), so the Agent's uncommitted edits are never seen; the answer carries
135
190
  `dirty: true` beside the output when the Worktree held some (a status in jr2's clean repository over a copy of the
@@ -148,35 +203,41 @@ Orchestrator's own process, the one Effect that does: it starts the run through
148
203
  Workspace with no Repo Slot has no commit to run it against, so there it is refused — by `jr2 up` and at invoke, with
149
204
  the fix: declare a slot, or name no secret, and it runs in `/work` beside the Agent (ADR-0051). A run of the live
150
205
  files under a credential is the claim this kind exists not to make.
151
- - **A `push` pushes the Workspace's branch to its Repo Slot's Binding**, from the Sandbox Effector, with no input:
152
- `push({ pin?, sign?, squash?, force?, slot?, timeout? })`. Its credential is the push credential of the
153
- `git.credentials` entry the Repo Slot's url falls under (ADR-0051): the provision matches the url the run binds —
154
- static, per-run or `customize()`-bound, one path — and names that entry's push Secret on the Sandbox, and the
155
- Effector, which holds only the entries named there, matches the push's url as the fence does. git never runs with the
156
- Agent's repository as its repository: each call makes a new bare repository in the Effector's scratch, configured by
157
- jr2 under a cleared environment; it reads the Agent's branch as data (a loose ref or `packed-refs`, opened beneath
158
- with no symlink, bounded); it imports the Agent's objects as data — one `pack-objects` over the Agent's object store
159
- with a deadline (a FIFO in place of an object blocks an open forever), `index-pack --strict`, and a connectivity check
160
- — borrowing only from the node's Repo cache; and it pushes `<commit>:refs/heads/<branch>` to the Binding url, never to
161
- a remote of the Agent's config, and never to a local path (git's local transport runs that repository's config and
162
- hooks, and a path in the pod can be the Agent's own repository; only the binary's tests can allow one). So no config,
163
- hook, attribute or program of the Agent's runs; a test plants each in the Agent's repository and proves none ran. A
164
- token goes Basic over https through a credential helper only — an `http://` url with a token is `rejected` with
165
- nothing sent — and the helper is the Effector binary (`effector git-credential`), which reads the token's file when
166
- git asks for the Binding's own host over https and answers nothing for any other; the clean repository follows no
167
- redirect, so neither the push nor the token reaches an origin outside the entry; an ssh key is copied into the call's
168
- directory (a Secret mount's mode is one ssh refuses), with `known_hosts` checked strictly. The commit is pinned when
169
- the push starts: the Agent's branch tip, or with `pin` the commit the input names (`{ sha }`); the Effector's first
170
- answer names it, and it is written to the intent row. A pinned commit the Agent pruned fails the import and is never
171
- substituted. `squash` makes one commit with the input's `{ message }`, on the published base: the Agent's tree, its
172
- author line and date, the Machine's message. It is a new commit, so it needs a committer. With `sign`, the signer is
173
- the committer, as for every signed copy. Without it (signing is optional, and `squash` is an option of its own), the
174
- matching `git.credentials` entry's `committer: { name, email }` commits it, at the Effector's clock — or, when the
175
- entry names no committer, its `sign` field's name and email. `jr2 up` refuses `squash` without `sign` only when the
176
- matched entry has neither `sign` nor `committer`, naming both fixes (a `committer`, or a signed push and its `sign`
177
- field), and the provision refuses it the same way for a slot the run or a `customize()` binds. `force` replaces what
178
- the remote holds. A Workspace with no Repo Slot has no branch to push, so a `push` there is refused, by `jr2 up` and
179
- at invoke (ADR-0051).
206
+ - **A `push` pushes a Worktree's branch to its Repo Slot's Binding**, from the Sandbox Effector:
207
+ `push({ pin?, sign?, squash?, force?, slot?, timeout? })`, with `worktree` in its input and no `to` and no `branch`.
208
+ `slot` stays static: a Machine with one slot leaves it out, and a Machine with two names it. The Orchestrator finds
209
+ the cut's settled row in the run's ledger, by the path and this Sandbox, and tells the Effector which branch to
210
+ publish. An Agent can change all of git in the pod, and it cannot change that row. With a read of HEAD,
211
+ `git switch main` would publish `main`. With an input key, a Chart could aim at any remote branch. A push with no row,
212
+ a row of another slot, or a detached Worktree is `rejected` at stage `start`. The ledger key is (slot, branch). The
213
+ local name equals the remote name, so an Agent, a Gate and a person in the pod all see the real branch. Its credential
214
+ is the push credential of the `git.credentials` entry the Repo Slot's url falls under (ADR-0051): the provision
215
+ matches the url the run binds — static, per-run or `customize()`-bound, one path — and names that entry's push Secret
216
+ on the Sandbox, and the Effector, which holds only the entries named there, matches the push's url as the fence does.
217
+ git never runs with the Agent's repository as its repository: each call makes a new bare repository in the Effector's
218
+ scratch, configured by jr2 under a cleared environment; it reads that branch as data (a loose ref or `packed-refs`,
219
+ opened beneath with no symlink, bounded); it imports the Agent's objects as data — one `pack-objects` over the Agent's
220
+ object store with a deadline (a FIFO in place of an object blocks an open forever), `index-pack --strict`, and a
221
+ connectivity check — borrowing only from the node's Repo cache; and it pushes `<commit>:refs/heads/<branch>` to the
222
+ Binding url, never to a remote of the Agent's config, and never to a local path (git's local transport runs that
223
+ repository's config and hooks, and a path in the pod can be the Agent's own repository; only the binary's tests can
224
+ allow one). So no config, hook, attribute or program of the Agent's runs; a test plants each in the Agent's repository
225
+ and proves none ran. A token goes Basic over https through a credential helper only — an `http://` url with a token is
226
+ `rejected` with nothing sent — and the helper is the Effector binary (`effector git-credential`), which reads the
227
+ token's file when git asks for the Binding's own host over https and answers nothing for any other; the clean
228
+ repository follows no redirect, so neither the push nor the token reaches an origin outside the entry; an ssh key is
229
+ copied into the call's directory (a Secret mount's mode is one ssh refuses), with `known_hosts` checked strictly. The
230
+ commit is pinned when the push starts: the tip of that branch, never HEAD, or with `pin` the commit the input names
231
+ (`{ sha }`); the Effector's first answer names it, and it is written to the intent row. A pinned commit the Agent
232
+ pruned fails the import and is never substituted. `squash` makes one commit with the input's `{ message }`, on the
233
+ published base: the Agent's tree, its author line and date, the Machine's message. It is a new commit, so it needs a
234
+ committer. With `sign`, the signer is the committer, as for every signed copy. Without it (signing is optional, and
235
+ `squash` is an option of its own), the matching `git.credentials` entry's `committer: { name, email }` commits it, at
236
+ the Effector's clock — or, when the entry names no committer, its `sign` field's name and email. `jr2 up` refuses
237
+ `squash` without `sign` only when the matched entry has neither `sign` nor `committer`, naming both fixes (a
238
+ `committer`, or a signed push and its `sign` field), and the provision refuses it the same way for a slot the run or a
239
+ `customize()` binds. `force` replaces what the remote holds. A Workspace with no Repo Slot has no branch to push, so a
240
+ `push` there is refused, by `jr2 up` and at invoke (ADR-0051).
180
241
  - **`sign`: nothing unsigned leaves.** The Effector re-creates the Agent's commits, each signed with the entry's `sign`
181
242
  key — ssh (`ssh-keygen -Y sign`) by default, GPG allowed; a GPG key with a passphrase is refused, by name. The author
182
243
  line, its date included, stays the Agent's; the committer is the entry's name and email at the Effector's clock. The
@@ -199,7 +260,7 @@ Orchestrator's own process, the one Effect that does: it starts the run through
199
260
  push and before anything is signed, the same map runs over the new commits, oldest first, and a commit that has a
200
261
  published twin is replaced by that twin in the rewritten history; only the unmapped commits are signed. The twin is a
201
262
  ledger pair, taken as it is — each names the one exact commit a push published for its source, a signed copy or a
202
- squashed commit; the pairs of every push of the Repo Slot are read, each only while the remote still holds its
263
+ squashed commit; the pairs of every push of the (slot, branch) are read, each only while the remote still holds its
203
264
  published commit, the newest push's when two name one source — or else a published commit after the commit's merge
204
265
  base with the remote's tip, each taken once, that has the commit's cherry key, read with the commit's parents as
205
266
  mapped so far (a merge of the Agent's own side branch keys on the published copies of its non-first parents), and is a
@@ -217,68 +278,69 @@ Orchestrator's own process, the one Effect that does: it starts the run through
217
278
  hold). A push that publishes nothing new carries one too: when `pair` or `cherry` maps the published tip itself (a
218
279
  restore that sends a pinned push again, a push after a sync that did not finish), or the published tip is this
219
280
  squash's own (a restore that sends a squash push again), the Agent's branch still holds its own commits. The bundle
220
- never reaches the Machine. A push sends every pair the ledger holds for its Workspace and Repo Slot — one branch on
221
- one remote — oldest push first (`pairs`, omitted when there is none): each push that published, with its published
222
- commit, its source and its `squashed`; the Effector stays stateless. The pair stands for the published tip only while
223
- the remote still holds that push: a remote that moved past it (another writer) is paired by `cherry` or is `diverged`,
224
- never grafted onto. The Agent is never told to rebase: a rebase repair loses a reworded message and duplicates empty
225
- commits and merges. A published commit is never signed twice, for every signer and key type: a side branch the Agent
226
- made before the push and merges later carries the unsigned original of a commit the remote holds signed, which
227
- ancestry calls new, and the map replaces it by its twin — a merge the Agent made before the first push included, and,
228
- through the ledger's pairs, a side branch made before a squash and merged by any later push, whose commits the
229
- squashed commit stands for: the squash's pair maps its source to the squashed commit however many pushes came after
230
- it, so a side branch merged by one push and a branch made on it merged by the next publish only what is new. The pair
231
- maps a squash's SOURCE only, and that is a named limit: a side branch made at an earlier commit of the squashed range
232
- publishes the commits it descends from as signed copies beside the squashed commit. Those commits were never
233
- published, none is signed twice, and the trees are the Agent's; such a commit has no faithful twin, and mapped to the
234
- squashed commit the push would publish a false diff. The committer date stays the Effector's clock; a deterministic
235
- re-sign (the Agent's committer date) was rejected, since it rests on one key type's property, not GPG's or ECDSA's. A
236
- key alone does not pair: a commit the Agent reverts and applies again with cherry-pick has the key of the first one,
237
- and the first one's published copy has older parents, so paired with it the push would offer that older copy as the
238
- new tip — `rejected` as non-fast-forward without `force`, a rewind of the remote with it. The faithful image is the
239
- rule; the cherry fallback keeps its own, that the last paired commit holds the published tip's tree. The map's cost is
240
- bounded by the push's size.
281
+ never reaches the Machine. A push sends every pair the ledger holds for its Workspace and its (slot, branch) — one
282
+ branch on one remote — oldest push first (`pairs`, omitted when there is none): each push that published, with its
283
+ published commit, its source and its `squashed`; the Effector stays stateless. The pair stands for the published tip
284
+ only while the remote still holds that push: a remote that moved past it (another writer) is paired by `cherry` or is
285
+ `diverged`, never grafted onto. The Agent is never told to rebase: a rebase repair loses a reworded message and
286
+ duplicates empty commits and merges. A published commit is never signed twice, for every signer and key type: a side
287
+ branch the Agent made before the push and merges later carries the unsigned original of a commit the remote holds
288
+ signed, which ancestry calls new, and the map replaces it by its twin — a merge the Agent made before the first push
289
+ included, and, through the ledger's pairs, a side branch made before a squash and merged by any later push, whose
290
+ commits the squashed commit stands for: the squash's pair maps its source to the squashed commit however many pushes
291
+ came after it, so a side branch merged by one push and a branch made on it merged by the next publish only what is
292
+ new. The pair maps a squash's SOURCE only, and that is a named limit: a side branch made at an earlier commit of the
293
+ squashed range publishes the commits it descends from as signed copies beside the squashed commit. Those commits were
294
+ never published, none is signed twice, and the trees are the Agent's; such a commit has no faithful twin, and mapped
295
+ to the squashed commit the push would publish a false diff. The committer date stays the Effector's clock; a
296
+ deterministic re-sign (the Agent's committer date) was rejected, since it rests on one key type's property, not GPG's
297
+ or ECDSA's. A key alone does not pair: a commit the Agent reverts and applies again with cherry-pick has the key of
298
+ the first one, and the first one's published copy has older parents, so paired with it the push would offer that older
299
+ copy as the new tip — `rejected` as non-fast-forward without `force`, a rewind of the remote with it. The faithful
300
+ image is the rule; the cherry fallback keeps its own, that the last paired commit holds the published tip's tree. The
301
+ map's cost is bounded by the push's size.
241
302
  - **The sync: after every push whose published commit is not the Agent's own, the Agent's branch moves to it** — signed
242
303
  copies, or one squashed commit, signed or not; the decision keys on `published` ≠ `source`, never on `sign` — in the
243
304
  Harness container (`POST /sync`, behind the Harness bearer, ADR-0058), from the bundle the Effector wrote, which the
244
305
  Orchestrator relays — never a shared volume: the Worktree stays read-only on the credentialed side. Under a cleared
245
- environment and a config that names no hook, no fsmonitor, no alternate-refs command and no maintenance, the Harness
246
- verifies and unbundles the bundle (objects only), checks that the published tip is a commit with the source's tree (it
247
- reads no signature), answers `busy` when a rebase of the branch, or a merge, cherry-pick, revert or sequence in a
248
- worktree whose HEAD is the branch, is in progress, and otherwise copies each commit the Agent made after the source as
249
- its raw object with only its parents changed, then moves the branch with one `git update-ref <new> <old>` — a
250
- compare-and-swap, tried again from the new tip at most 3 times — so files, index, HEAD and stash do not change. Its
251
- answer is a field of the push's output beside the outcome: `sync: synced | already | skipped | busy | not-synced`,
252
- with `syncReason` when it moved nothing (a push that published the Agent's own commit is `skipped`, with no call and
253
- no bundle; every other push is synced, one that published nothing new included, so a plain push of the slot after a
254
- squash is a fast-forward of the squashed commit). A Harness that cannot be reached answers `not-synced`; the push
255
- stays `pushed`. When the last push of a Repo Slot answered `busy` or `not-synced`, jr2 runs its sync again before that
256
- slot's next push, in that slot's Worktree and with that push's bundle, which its row keeps until a sync finished or a
257
- newer push of the slot published. The push never trusts the sync's answer: the Agent owns its repository and can move
258
- the branch at any time, so the next push derives everything again from the remote and the Agent's objects. After such
259
- a push the Agent reads, beside the output, what moved — signed copies of its commits with new ids, or its commits
260
- squashed into one commit, named "signed" only when it is — and that jr2 moved its branch to it, or did not and why;
261
- that its files, index and stash did not change; and that amending, rebasing or resetting the pushed commits makes the
262
- next push `diverged`. The Harness keys that paragraph on a synced push whose published commit is not the Agent's, and
263
- its words on `squashed`. A push in which jr2 counted no new commit (`newCommits: 0`) is still `pushed`, because the
264
- push ran and the remote's branch may have moved (a new branch at a commit the remote held on another branch). But
265
- `pushed` alone does not tell the Agent that its work did not go out: in a real Haiku 5.5 run, the model sent a
266
- `git commit` that failed and the push in one answer, the push published the branch at a commit the remote already
267
- held, and the model read `pushed` as its work published. So beside the output the Agent reads that jr2 counted no new
268
- commit in this push, that it found the published commit already on the remote or in jr2's copy of the remote, and that
269
- a change it did not commit before the push is not in this push. The words name where jr2 looked because the count
270
- reads the Repo cache, which fetches without pruning: a commit the remote dropped can stay there, and then a count of 0
271
- is not a fact about the remote, which is why the first clause names what jr2 counted. No @model claim reaches
272
- `newCommits: 0`, so no measurement has read these words. When the signed-copies or squash paragraph above also
273
- applies, it names "an earlier push" as the one that signed or squashed, so the two do not contradict. The Harness keys
274
- the sentence on `newCommits: 0` alone, and says nothing with no count. The count is optional on the wire: a live
275
- Sandbox keeps the runtime it started with (ADR-0037), so after `jr2 up` its older Effector answers `pushed` with no
276
- `newCommits`, and a required field would fault a push that reached the remote. The count's input to
277
- `git rev-list --stdin` is revisions only (`^<sha>` for each tip the remote held), never `--not`: git takes an option
278
- there only from 2.42, and the Sandbox Effector runs its image's git — the Sandbox Image's, unless the `workspace()`
279
- names one — which can be older (Debian bookworm's is 2.39). A distinct outcome (`unchanged`, `up-to-date`) was
280
- rejected: every consumer of a push keys on `pushed` (the ledger's pairs, the sync, the Machines' guards), and a push
281
- that made a new branch did change the remote.
306
+ environment and a config that names no hook, no fsmonitor, no alternate-refs command and no maintenance — and with its
307
+ git confined, as every process it starts in `/work` (ADR-0076), because a filter or a textconv in the shared
308
+ `.git/config` runs in git's process — the Harness verifies and unbundles the bundle (objects only), checks that the
309
+ published tip is a commit with the source's tree (it reads no signature), answers `busy` when a rebase of the branch,
310
+ or a merge, cherry-pick, revert or sequence in a worktree whose HEAD is the branch, is in progress, and otherwise
311
+ copies each commit the Agent made after the source as its raw object with only its parents changed, then moves the
312
+ branch with one `git update-ref <new> <old>` — a compare-and-swap, tried again from the new tip at most 3 times — so
313
+ files, index, HEAD and stash do not change. Its answer is a field of the push's output beside the outcome:
314
+ `sync: synced | already | skipped | busy | not-synced`, with `syncReason` when it moved nothing (a push that published
315
+ the Agent's own commit is `skipped`, with no call and no bundle; every other push is synced, one that published
316
+ nothing new included, so a plain push of the slot after a squash is a fast-forward of the squashed commit). A Harness
317
+ that cannot be reached answers `not-synced`; the push stays `pushed`. When the last push of a (slot, branch) answered
318
+ `busy` or `not-synced`, jr2 runs its sync again before the next push of that key, in the Worktree of that push and
319
+ with its bundle, which its row keeps until a sync finished or a newer push of that key published. The push never
320
+ trusts the sync's answer: the Agent owns its repository and can move the branch at any time, so the next push derives
321
+ everything again from the remote and the Agent's objects. After such a push the Agent reads, beside the output, what
322
+ moved — signed copies of its commits with new ids, or its commits squashed into one commit, named "signed" only when
323
+ it is — and that jr2 moved its branch to it, or did not and why; that its files, index and stash did not change; and
324
+ that amending, rebasing or resetting the pushed commits makes the next push `diverged`. The Harness keys that
325
+ paragraph on a synced push whose published commit is not the Agent's, and its words on `squashed`. A push in which jr2
326
+ counted no new commit (`newCommits: 0`) is still `pushed`, because the push ran and the remote's branch may have moved
327
+ (a new branch at a commit the remote held on another branch). But `pushed` alone does not tell the Agent that its work
328
+ did not go out: in a real Haiku 5.5 run, the model sent a `git commit` that failed and the push in one answer, the
329
+ push published the branch at a commit the remote already held, and the model read `pushed` as its work published. So
330
+ beside the output the Agent reads that jr2 counted no new commit in this push, that it found the published commit
331
+ already on the remote or in jr2's copy of the remote, and that a change it did not commit before the push is not in
332
+ this push. The words name where jr2 looked because the count reads the Repo cache, which fetches without pruning: a
333
+ commit the remote dropped can stay there, and then a count of 0 is not a fact about the remote, which is why the first
334
+ clause names what jr2 counted. No @model claim reaches `newCommits: 0`, so no measurement has read these words. When
335
+ the signed-copies or squash paragraph above also applies, it names "an earlier push" as the one that signed or
336
+ squashed, so the two do not contradict. The Harness keys the sentence on `newCommits: 0` alone, and says nothing with
337
+ no count. The count is optional on the wire: a live Sandbox keeps the runtime it started with (ADR-0037), so after
338
+ `jr2 up` its older Effector answers `pushed` with no `newCommits`, and a required field would fault a push that
339
+ reached the remote. The count's input to `git rev-list --stdin` is revisions only (`^<sha>` for each tip the remote
340
+ held), never `--not`: git takes an option there only from 2.42, and the Sandbox Effector runs its image's git — the
341
+ Sandbox Image's, unless the `workspace()` names one — which can be older (Debian bookworm's is 2.39). A distinct
342
+ outcome (`unchanged`, `up-to-date`) was rejected: every consumer of a push keys on `pushed` (the ledger's pairs, the
343
+ sync, the Machines' guards), and a push that made a new branch did change the remote.
282
344
  - **The process group ends with the exec.** Each exec leads its own group; its timeout, a kill, and its top process's
283
345
  exit each SIGKILL the group, so nothing that stays in the group outlives it. In the Harness container a descendant
284
346
  that leaves the group (`setsid`) is not killed; it holds no credential. A credentialed command's descendant cannot
@@ -292,30 +354,33 @@ Orchestrator's own process, the one Effect that does: it starts the run through
292
354
  carries that transition, so a restart that loses the transition's save still delivers the output and runs nothing. On
293
355
  restore: settled → the output is delivered; no row → nothing was sent, so it starts; intent → per kind. A `command`
294
356
  that names no secret re-attaches to the exec by id, or settles `unknown` when the Harness no longer knows it (it
295
- restarted); it is never run again by jr2: it runs the Agent's code, which may spend a Stand-in. A `push` intent row
296
- names its Sandbox and, once the Effector's first answer names it, the pinned commit: a restore asks the Effector for
297
- the id and follows it, and when the Effector no longer holds it the push is sent again with only that commit — never
298
- the Agent's tip now — because its run is a function of the commit and its credential, what the intent row records;
299
- with no commit recorded it settles `unknown`. A credentialed command follows the push's rule: its intent row names its
300
- Sandbox and, once the first answer names it, the pinned commit, and one the Effector lost is sent again with only that
301
- commit, since its run is a function of (commit, argv, secrets). An `http` intent row names its placement — the
302
- Instance Effector, or the Sandbox of a file-body `http` — and no replica: the Effect asks every replica of that
303
- Effector for its id and follows the one that holds it; when none does, it is sent again only when the author marked it
304
- `idempotent`, and any other settles `unknown`. Retry is the author's choice — a route back into the state, or
305
- `idempotent` for a call that carries its own idempotency key or only reads. No row is dropped: each settled or aborted
306
- row keeps the Effect's id, kind and outcome for the run's life, the record of what the run's Effects did. A settled
307
- row's output tail is dropped once the save that carries its transition is on disk; the full stream is on the feed. An
308
- `http` response's body and headers are its tail: the row keeps the outcome, the status, the content type and the size,
309
- and the settled line on the feed carries the whole response. A `push` row keeps its Repo Slot, its published commit,
310
- its source, its `squashed` and its sync's answer whole — every later push of its Workspace and Repo Slot sends them as
311
- one of its pairs, and the newest push's sync, when it did not finish, runs again — and drops git's words; it drops its
312
- bundle once its sync answered `synced`, `already` or `skipped`, and keeps it while the sync is `busy` or `not-synced`
313
- only as long as it is the newest push of its Repo Slot that published, because the sync that runs again moves the
314
- branch from it and only the newest push's sync runs again. Once a newer push of the slot published, the older row's
315
- bundle goes at the next save: one bundle per Repo Slot at most. So an Effect in a loop grows the ledger by one short
316
- row per pass, never by its tails. One exception, bounded by one pick: while a pick is outstanding, its hold keeps the
317
- `chain` whole, tails included, because an `answer` action may read it after a restore; a loop below the Agent's state
318
- then grows the hold by one tail per pass. The hold drops its chain when the pick is answered.
357
+ restarted); it is never run again by jr2: it runs the Agent's code, which may spend a Stand-in. A cut is the
358
+ exception: it runs the kit's program and is marked idempotent, so a restore or a Harness restart sends it again with
359
+ the same id. A `push` intent row names its Sandbox and, once the Effector's first answer names it, the pinned commit:
360
+ a restore asks the Effector for the id and follows it, and when the Effector no longer holds it the push is sent again
361
+ with only that commit — never the Agent's tip now — because its run is a function of the commit and its credential,
362
+ what the intent row records; with no commit recorded it settles `unknown`. A credentialed command follows the push's
363
+ rule: its intent row names its Sandbox and, once the first answer names it, the pinned commit, and one the Effector
364
+ lost is sent again with only that commit, since its run is a function of (commit, argv, secrets). An `http` or `mcp`
365
+ intent row names its placement — the Instance Effector, or the Sandbox of a file-body `http` — and no replica: the
366
+ Effect asks every replica of that Effector for its id and follows the one that holds it; when none does, it is sent
367
+ again only when the author marked it `idempotent`, and any other settles `unknown`. Retry is the author's choice — a
368
+ route back into the state, or `idempotent` for a call that carries its own idempotency key or only reads. No row is
369
+ dropped: each settled or aborted row keeps the Effect's id, kind and outcome for the run's life, the record of what
370
+ the run's Effects did. A settled row's output tail is dropped once the save that carries its transition is on disk;
371
+ the full stream is on the feed. An `http` response's body and headers are its tail: the row keeps the outcome, the
372
+ status, the content type and the size, and the settled line on the feed carries the whole response. A `push` row keeps
373
+ its Repo Slot, its branch, its published commit, its source, its `squashed` and its sync's answer whole — every later
374
+ push of its Workspace and (slot, branch) sends them as one of its pairs, and the newest push's sync, when it did not
375
+ finish, runs again — and drops git's words; it drops its bundle once its sync answered `synced`, `already` or
376
+ `skipped`, and keeps it while the sync is `busy` or `not-synced` only as long as it is the newest push of its (slot,
377
+ branch) that published, because the sync that runs again moves the branch from it and only the newest push's sync runs
378
+ again. Once a newer push of that key published, the older row's bundle goes at the next save: one bundle per (slot,
379
+ branch) at most. A cut's `ready` row keeps its Repo Slot, its path and its branch whole, because a later Effect reads
380
+ them. So an Effect in a loop grows the ledger by one short row per pass, never by its tails. One exception, bounded by
381
+ one pick: while a pick is outstanding, its hold keeps the `chain` whole, tails included, because an `answer` action
382
+ may read it after a restore; a loop below the Agent's state then grows the hold by one tail per pass. The hold drops
383
+ its chain when the pick is answered.
319
384
  - **A start is sent once where it may have arrived.** A Harness runs one id once only within its own process; a
320
385
  restarted Harness knows no id. So the Orchestrator sends a start again only when it certainly never left (the
321
386
  connection was refused, the name did not resolve). Any other failure — a reset, a 5xx, no answer — may follow a start
@@ -374,6 +439,9 @@ Orchestrator's own process, the one Effect that does: it starts the run through
374
439
  binds the argv, refuses there a param the bound argv does not read, and gives the bound Machine the warnings the walk
375
440
  gives for that argv (ADR-0054).
376
441
 
442
+ - **The load walk refuses an Effect's `onDone` with no unguarded last arm.** A type cannot see a missing arm, and with
443
+ no match a run stays in that state and shows no fault.
444
+
377
445
  ## Considered options
378
446
 
379
447
  - **Run every command in the Sandbox Effector.** Rejected: a command that names no secret runs the Workspace's own code