@jr2/orchestrator 0.6.0 → 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 (301) 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 +86 -9
  14. package/docs/concepts/architecture.md +41 -28
  15. package/docs/concepts/callers-and-hooks.md +205 -0
  16. package/docs/concepts/charts.md +7 -5
  17. package/docs/concepts/composition.md +10 -1
  18. package/docs/concepts/converge.md +4 -2
  19. package/docs/concepts/durable-runs.md +43 -16
  20. package/docs/concepts/effects.md +50 -22
  21. package/docs/concepts/gates.md +95 -25
  22. package/docs/concepts/machines-and-workflows.md +5 -4
  23. package/docs/concepts/pools-and-sources.md +187 -11
  24. package/docs/concepts/secrets-and-trust.md +73 -33
  25. package/docs/concepts/the-menu.md +11 -0
  26. package/docs/concepts/workspaces.md +6 -5
  27. package/docs/get-started/a-model-decides.md +14 -13
  28. package/docs/get-started/an-agent-in-a-sandbox.md +43 -34
  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/guides/add-tools-to-the-sandbox.md +55 -11
  32. package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
  33. package/docs/guides/answer-a-gate.md +74 -37
  34. package/docs/guides/bind-open-parts.md +24 -8
  35. package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
  36. package/docs/guides/cancel-a-run.md +26 -16
  37. package/docs/guides/fix-a-failed-converge.md +18 -6
  38. package/docs/guides/give-a-caller-a-scope.md +203 -0
  39. package/docs/guides/give-an-agent-mcp-tools.md +422 -0
  40. package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
  41. package/docs/guides/let-an-agent-write-a-chart.md +9 -6
  42. package/docs/guides/look-inside-a-sandbox.md +101 -31
  43. package/docs/guides/open-the-console.md +63 -14
  44. package/docs/guides/read-a-conversation.md +225 -0
  45. package/docs/guides/run-a-chart-by-name.md +17 -16
  46. package/docs/guides/run-one-worker-per-item.md +15 -9
  47. package/docs/guides/set-an-agents-access-and-dials.md +2 -0
  48. package/docs/guides/start-a-run-for-each-alert.md +290 -0
  49. package/docs/guides/start-a-run-from-a-run.md +18 -16
  50. package/docs/guides/start-a-run.md +24 -18
  51. package/docs/guides/steer-an-agent.md +332 -0
  52. package/docs/guides/use-a-hosted-provider.md +1 -1
  53. package/docs/guides/watch-a-run.md +127 -121
  54. package/docs/guides/write-a-hook-kind.md +238 -0
  55. package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
  56. package/docs/guides/write-a-workflow.md +17 -7
  57. package/docs/llms.txt +81 -15
  58. package/docs/reference/authoring-surface/agent.md +1 -1
  59. package/docs/reference/authoring-surface/answer.md +1 -1
  60. package/docs/reference/authoring-surface/arg.md +1 -1
  61. package/docs/reference/authoring-surface/basic.md +1 -1
  62. package/docs/reference/authoring-surface/bearer.md +1 -1
  63. package/docs/reference/authoring-surface/branchname.md +1 -1
  64. package/docs/reference/authoring-surface/carry.md +23 -0
  65. package/docs/reference/authoring-surface/chartschema.md +1 -1
  66. package/docs/reference/authoring-surface/chartschemafor.md +1 -1
  67. package/docs/reference/authoring-surface/command.md +1 -1
  68. package/docs/reference/authoring-surface/customize.md +4 -1
  69. package/docs/reference/authoring-surface/defineconfig.md +1 -1
  70. package/docs/reference/authoring-surface/defineevent.md +1 -1
  71. package/docs/reference/authoring-surface/dispatch.md +5 -4
  72. package/docs/reference/authoring-surface/fail.md +1 -1
  73. package/docs/reference/authoring-surface/file.md +1 -1
  74. package/docs/reference/authoring-surface/frompick.md +1 -1
  75. package/docs/reference/authoring-surface/gate.md +118 -0
  76. package/docs/reference/authoring-surface/group-agents.md +15 -0
  77. package/docs/reference/authoring-surface/group-charts.md +16 -0
  78. package/docs/reference/authoring-surface/group-composition.md +25 -0
  79. package/docs/reference/authoring-surface/group-config.md +17 -0
  80. package/docs/reference/authoring-surface/group-effects.md +48 -0
  81. package/docs/reference/authoring-surface/group-failure.md +13 -0
  82. package/docs/reference/authoring-surface/group-gates.md +16 -0
  83. package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
  84. package/docs/reference/authoring-surface/group-steering.md +12 -0
  85. package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
  86. package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
  87. package/docs/reference/authoring-surface/http.md +1 -1
  88. package/docs/reference/authoring-surface/isfailure.md +1 -1
  89. package/docs/reference/authoring-surface/isready.md +1 -1
  90. package/docs/reference/authoring-surface/jr2setup.md +13 -3
  91. package/docs/reference/authoring-surface/mcp.md +107 -0
  92. package/docs/reference/authoring-surface/open.md +5 -3
  93. package/docs/reference/authoring-surface/openbody.md +1 -1
  94. package/docs/reference/authoring-surface/paramsof.md +1 -1
  95. package/docs/reference/authoring-surface/pool.md +77 -9
  96. package/docs/reference/authoring-surface/push.md +1 -1
  97. package/docs/reference/authoring-surface/readyof.md +1 -1
  98. package/docs/reference/authoring-surface/reread.md +19 -0
  99. package/docs/reference/authoring-surface/secret.md +1 -1
  100. package/docs/reference/authoring-surface/sentby.md +1 -1
  101. package/docs/reference/authoring-surface/skip.md +22 -0
  102. package/docs/reference/authoring-surface/source.md +56 -1
  103. package/docs/reference/authoring-surface/steer.md +93 -0
  104. package/docs/reference/authoring-surface/text.md +1 -1
  105. package/docs/reference/authoring-surface/type-agentdefinition.md +14 -1
  106. package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
  107. package/docs/reference/authoring-surface/type-anyframepath.md +1 -1
  108. package/docs/reference/authoring-surface/type-anyworktreepath.md +1 -1
  109. package/docs/reference/authoring-surface/type-binding.md +1 -1
  110. package/docs/reference/authoring-surface/type-caller.md +40 -0
  111. package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
  112. package/docs/reference/authoring-surface/type-carry.md +19 -0
  113. package/docs/reference/authoring-surface/type-chainlink.md +3 -3
  114. package/docs/reference/authoring-surface/type-commandoutput.md +1 -1
  115. package/docs/reference/authoring-surface/type-customize.md +16 -1
  116. package/docs/reference/authoring-surface/type-delivered.md +17 -0
  117. package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
  118. package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
  119. package/docs/reference/authoring-surface/type-dispatchoutput.md +1 -1
  120. package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
  121. package/docs/reference/authoring-surface/type-framepath.md +1 -1
  122. package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
  123. package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
  124. package/docs/reference/authoring-surface/type-gatelook.md +37 -0
  125. package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
  126. package/docs/reference/authoring-surface/type-gatereply.md +19 -0
  127. package/docs/reference/authoring-surface/type-gateslot.md +21 -0
  128. package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
  129. package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
  130. package/docs/reference/authoring-surface/type-gateslots.md +12 -0
  131. package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
  132. package/docs/reference/authoring-surface/type-hostinjectedinput.md +1 -1
  133. package/docs/reference/authoring-surface/type-httpoutput.md +1 -1
  134. package/docs/reference/authoring-surface/type-jr2config.md +37 -2
  135. package/docs/reference/authoring-surface/type-lateroutcome.md +1 -1
  136. package/docs/reference/authoring-surface/type-lends.md +5 -1
  137. package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
  138. package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
  139. package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
  140. package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
  141. package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
  142. package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
  143. package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
  144. package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
  145. package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
  146. package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
  147. package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
  148. package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
  149. package/docs/reference/authoring-surface/type-poolnextinput.md +1 -1
  150. package/docs/reference/authoring-surface/type-poolspec.md +11 -6
  151. package/docs/reference/authoring-surface/type-pushoutput.md +1 -1
  152. package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
  153. package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
  154. package/docs/reference/authoring-surface/type-reposlot.md +1 -1
  155. package/docs/reference/authoring-surface/type-reread.md +18 -0
  156. package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
  157. package/docs/reference/authoring-surface/type-sender.md +26 -1
  158. package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
  159. package/docs/reference/authoring-surface/type-size.md +1 -1
  160. package/docs/reference/authoring-surface/type-skip.md +19 -0
  161. package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
  162. package/docs/reference/authoring-surface/type-sourcein.md +25 -0
  163. package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
  164. package/docs/reference/authoring-surface/type-sourcespec.md +16 -7
  165. package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
  166. package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
  167. package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
  168. package/docs/reference/authoring-surface/type-steerargs.md +20 -0
  169. package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
  170. package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
  171. package/docs/reference/authoring-surface/type-workspaced.md +1 -1
  172. package/docs/reference/authoring-surface/type-worktreefound.md +3 -3
  173. package/docs/reference/authoring-surface/type-worktreeinput.md +1 -1
  174. package/docs/reference/authoring-surface/type-worktreeoptions.md +1 -1
  175. package/docs/reference/authoring-surface/type-worktreeoutput.md +1 -1
  176. package/docs/reference/authoring-surface/type-worktreepath.md +1 -1
  177. package/docs/reference/authoring-surface/type-worktreeready.md +1 -1
  178. package/docs/reference/authoring-surface/type-worktreerefused.md +4 -4
  179. package/docs/reference/authoring-surface/workspace.md +1 -1
  180. package/docs/reference/authoring-surface/worktree.md +1 -1
  181. package/docs/reference/authoring-surface.md +88 -167
  182. package/docs/reference/cli.md +413 -74
  183. package/docs/reference/config.md +235 -2
  184. package/docs/reference/decisions/0002-duplex-streaming-actor.md +71 -11
  185. package/docs/reference/decisions/0005-sandbox-pod-composition.md +14 -10
  186. package/docs/reference/decisions/0006-agent-control-surface.md +4 -4
  187. package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
  188. package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
  189. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
  190. package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
  191. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +9 -2
  192. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +20 -14
  193. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
  194. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +18 -14
  195. package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -12
  196. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
  197. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +32 -18
  198. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +14 -1
  199. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
  200. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +116 -34
  201. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +38 -37
  202. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
  203. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +7 -6
  204. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +23 -7
  205. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
  206. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
  207. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -18
  208. package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
  209. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +4 -3
  210. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
  211. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +5 -4
  212. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +19 -19
  213. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
  214. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
  215. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
  216. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +7 -0
  217. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +13 -7
  218. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +38 -30
  219. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +64 -17
  220. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +2 -1
  221. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
  222. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
  223. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +51 -23
  224. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +94 -54
  225. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +101 -47
  226. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
  227. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +17 -13
  228. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
  229. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
  230. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +4 -3
  231. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +37 -10
  232. package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
  233. 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
  234. package/docs/reference/decisions.md +6 -4
  235. package/docs/reference/glossary.md +96 -42
  236. package/docs/reference/hooks.md +1077 -0
  237. package/docs/reference/http-api.md +976 -149
  238. package/docs/why.md +2 -1
  239. package/package.json +10 -3
  240. package/src/actor.ts +36 -10
  241. package/src/agent.ts +50 -0
  242. package/src/assembly-checks.ts +530 -4
  243. package/src/callers.ts +245 -0
  244. package/src/chart.ts +52 -7
  245. package/src/config.ts +550 -1
  246. package/src/conversations.ts +453 -0
  247. package/src/custodian.ts +8 -2
  248. package/src/customize.ts +143 -4
  249. package/src/dispatch-effect.ts +142 -26
  250. package/src/durability.ts +2 -0
  251. package/src/duration.ts +20 -0
  252. package/src/effect-checks.ts +2 -1
  253. package/src/effect.ts +60 -7
  254. package/src/effector-wire.ts +272 -27
  255. package/src/gate-brand.ts +16 -0
  256. package/src/gate.ts +634 -48
  257. package/src/harness-client.ts +10 -4
  258. package/src/held-secrets.ts +72 -23
  259. package/src/holds.ts +2 -2
  260. package/src/hook-kinds/discord.ts +92 -0
  261. package/src/hook-kinds/door.ts +107 -0
  262. package/src/hook-kinds/github.ts +89 -0
  263. package/src/hook-kinds/gitlab.ts +72 -0
  264. package/src/hook-kinds/hook.ts +7 -0
  265. package/src/hook-kinds/index.ts +465 -0
  266. package/src/hook-kinds/slack.ts +151 -0
  267. package/src/hook-kinds/webhook.ts +113 -0
  268. package/src/hooks.ts +506 -0
  269. package/src/http-effect.ts +76 -38
  270. package/src/http.ts +889 -94
  271. package/src/index.ts +16 -5
  272. package/src/instance.ts +196 -26
  273. package/src/internal.ts +22 -3
  274. package/src/machine-doc.ts +26 -1
  275. package/src/mcp-effect.ts +424 -0
  276. package/src/mount-digest.ts +41 -7
  277. package/src/names.ts +64 -2
  278. package/src/notices.ts +138 -10
  279. package/src/open-body.ts +25 -11
  280. package/src/open-effect.ts +17 -4
  281. package/src/open.ts +4 -2
  282. package/src/parts.ts +87 -9
  283. package/src/party.ts +2 -1
  284. package/src/pick.ts +2 -2
  285. package/src/pool.ts +1142 -163
  286. package/src/registration.ts +203 -13
  287. package/src/run-host.ts +1509 -171
  288. package/src/run-list.ts +191 -0
  289. package/src/sandbox-kube.ts +53 -5
  290. package/src/secret-placement.ts +239 -33
  291. package/src/server.ts +53 -4
  292. package/src/setup.ts +72 -32
  293. package/src/snapshot-store.ts +565 -48
  294. package/src/steer.ts +111 -0
  295. package/src/stub-harness.ts +6 -2
  296. package/src/tokens.ts +263 -22
  297. package/src/wire.ts +17 -0
  298. package/src/workspace.ts +50 -6
  299. package/src/worktree.ts +3 -3
  300. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
  301. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
@@ -48,9 +48,13 @@ composition and does not replace it. The two uses stay two mechanisms.
48
48
  the url and the token together, one Open part, with `customize(m, { effects: { task: { target: { url, token } } } })`
49
49
  ([ADR-0054](0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)); `secret(open)` inside a
50
50
  target is refused, since the whole target is the part. Another Instance may run another Kit version, so the HTTP door
51
- is the only contract there. The token is the Instance token, which unlocks all control
52
- ([ADR-0032](0032-the-console-unlocks-with-the-instance-token.md)); a start-only token is a token kind of its own and
53
- is deferred.
51
+ is the only contract there. The token is any Caller's credential of the other Instance with `start` for the callee
52
+ Workflow ([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): a named Caller
53
+ token made for this, or that Instance's Instance token. A `wait` also polls `GET /runs/<id>`, so its token needs
54
+ `read` for that Workflow too. The start carries the first Caller of the calling run's `startedBy` chain as
55
+ `onBehalfOf`, in the header `jr2-on-behalf-of`, which the other Instance records and does not check: that Caller's own
56
+ `onBehalfOf`, else the name that jr2 shows, else `<via>/<name>`. A chain that jr2 started, or a text that is not
57
+ printable ASCII of at most 256 characters, sends no claim.
54
58
  - **Two settlements, `started` by default, and every outcome is output.** `{ outcome: "started", run }` settles when the
55
59
  Orchestrator accepts the run; the watcher goes round again. `wait: true` settles
56
60
  `{ outcome: "settled", run, status, output }` when that run ends, `status` in the store's own words (`done`, `failed`,
@@ -69,12 +73,12 @@ composition and does not replace it. The two uses stay two mechanisms.
69
73
  a DELETE of the Effect ends that poll and never touches the run. No callback from the callee: a callback needs the
70
74
  callee to hold its caller's address and a token for it, which inverts the trust. A door refusal is OUTPUT,
71
75
  `{ outcome: "refused", status, report }`, with the door's status — 400 for bad input, 404 for a name the current
72
- Assembly lacks, 409 for an unrunnable named Chart or a key another Workflow's run holds, 422 for a Chart the door
73
- refuses, 413 for a start over the bytes an Effector reads, 0 when the Effector refused it before anything was sent —
74
- as a Chart refusal is ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)): the body may have come from a model,
75
- and the report must be able to reach it through the Chain. A target no replica could reach is
76
- `{ outcome: "unreachable" }`, output, as an `http` Effect's is (ADR-0067): nothing was started. The invoke's error is
77
- kept for defects: no run host or Effector, input that fails the slot's schema, a refused or malformed wire.
76
+ Assembly lacks, 409 for an unrunnable named Chart, 422 for a Chart the door refuses, 413 for a start over the bytes an
77
+ Effector reads, 0 when the Effector refused it before anything was sent — as a Chart refusal is
78
+ ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)): the body may have come from a model, and the report must
79
+ be able to reach it through the Chain. A target no replica could reach is `{ outcome: "unreachable" }`, output, as an
80
+ `http` Effect's is (ADR-0067): nothing was started. The invoke's error is kept for defects: no run host or Effector,
81
+ input that fails the slot's schema (when no run has the key, below), a refused or malformed wire.
78
82
  - **The two runs are independent.** Cancelling the caller never cancels the callee; the caller's end never ends it;
79
83
  leaving the state stops a wait and never the run; the callee's Workspaces are its own. Temporal's parent-close policy
80
84
  (`TERMINATE` by default, `REQUEST_CANCEL`, `ABANDON`) exists because a child there is both a run and a child, and its
@@ -89,7 +93,8 @@ composition and does not replace it. The two uses stay two mechanisms.
89
93
  `dispatched [{ slot, run, at }]`, the chain's other direction. `jr2 runs` carries `dispatchedBy` after the id,
90
94
  `jr2 status` prints `dispatched by:` and `dispatched:` lines, and the Console shows both. The word is **dispatched
91
95
  by**. The callee reads nothing of its caller from its input: a Machine that wants to tell its callee something passes
92
- it as input like any caller.
96
+ it as input like any caller. A local dispatch records its caller's run as the callee's `startedBy`,
97
+ `{ kind: "run", run, slot }`, and `jr2 status` walks that chain to the first Caller (ADR-0078).
93
98
  - **A chain of dispatches is unbounded.** A run may dispatch a Workflow that dispatches another, or itself: a run that
94
99
  dispatches its own successor and ends is Temporal's continue-as-new, the honest way to keep a watcher's snapshot
95
100
  small, and a depth ceiling would refuse it. A runaway chain is a resource problem the cluster owner's quota and
@@ -100,24 +105,45 @@ composition and does not replace it. The two uses stay two mechanisms.
100
105
  key, out of band — the `jr2-dispatched-by` header, the `dispatchedBy` record as JSON, never a body field the callee's
101
106
  door would have to reserve — and `POST /workflows/<name>/runs` asks the key before anything else, so a re-sent start
102
107
  answers its run even when the name would be refused now: 201 with the run for a new one, 200 with
103
- `{ runId, instanceId, existing: true }` for a repeat, 409 when a run of ANOTHER Workflow holds the key (the caller
104
- asked for one Workflow and would wait on a run of another), 400 when the header does not parse, and 500, never 404,
105
- for any other throw, since a foreign caller reads a 404 as "nothing was started" (ADR-0033 as amended). The key lives
106
- in the callee's blob, and the store copies it into a `dispatch_effect` column of its own at every save and indexes
107
- that, not an expression index over `json_extract`: SQLite's JSON parser refuses a blob nested about a thousand deep,
108
- and such an index would fail that row's save and every later open of the volume. A run restore marks `lost` keeps its
109
- row and its blob, so its key keeps answering; the weekly sweep
110
- ([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-a-week.md)) removes the key with the row, and a re-send
111
- after it starts a new run: known, and not guarded. In-process the ledger is consulted directly, through the same start
112
- under the same key, with the starts in flight held so two arrivals of one key before the first save are one run. So a
113
- dispatch is idempotent whatever its author writes: its intent row says `idempotent: true`, a restore that cannot tell
114
- whether its start arrived sends it again under its key with what is left of the bound, the Instance Effector sends a
115
- start whose answer was lost again under its key with backoff until the bound, and the Effector's rollout grace is
116
- never stretched by a dispatch's wait. Once the answer names the run, the intent row carries it, the Effector's
117
- `running` state carries it on the wire, and a restore with it starts nothing: it settles `started` at once, or resumes
118
- the wait. Without it a watcher double-starts a run after a restart, which a human notices as two pull requests. What
119
- it does not guard: a door at a Kit version before this decision ignores the header, so a re-send to it starts a second
120
- run, and neither the Effector nor the preflight can tell. A capability check is named and deferred.
108
+ `{ runId, instanceId, existing: true }` for a repeat, 200 with `{ existing: false }` for a key-only ask (below) with
109
+ no run under the key, 400 when a header does not parse, and 500, never 404, for any other throw, since a foreign
110
+ caller reads a 404 as "nothing was started" (ADR-0033 as amended). **The key is scoped to the callee Workflow**: the
111
+ store finds a key with the Workflow's name, so the same key in two Workflows is two runs, and a key never answers with
112
+ a run of another Workflow. A key answers `existing` while its run's row lives, in any status: live, and for
113
+ `runs.retain` after it ends ([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)). A
114
+ retry after a failure is then a person's act, not a re-sent start's. The Effect checks the key before it checks its
115
+ input against the slot's schema, in-process and on the foreign path, as the door does: a start sent again across a
116
+ change of the callee's door answers `existing`, not an error. In-process it asks the run host for the key before it
117
+ reads the input, within the slot's bound. On the foreign path, a body that the slot's schema refuses is never sent:
118
+ the start is a **key-only ask**, the header `jr2-key-only: true` beside `jr2-dispatched-by` and no body, out of band
119
+ as the key is. The door keeps its order: the scope check (ADR-0078), then the key. With a run under the key it answers
120
+ `existing`, as for any start; with none it starts nothing and answers 200 with `{ existing: false }`, which a foreign
121
+ caller cannot read as a refused name (404) or a defect (5xx). The Instance Effector settles that answer as `absent`,
122
+ an outcome of the wire alone, and the Effect makes it the slot's schema refusal, the invoke's error. So input that
123
+ fails the slot's schema starts nothing on either path, and a run that its key already started is answered on both. A
124
+ body that the slot's schema takes goes as before, in one request. This is the same for a first send and for a send
125
+ again (a restore, a retry): a Pool's hand-over is a first send of a new run's Effect under a key that may already name
126
+ a run of a callee whose door changed. A `wait` sent again under a key that answers `existing` follows that run, and
127
+ settles when it ends. **A Pool's worker sends its `itemId` as the key** ([ADR-0017](0017-pool-over-a-source-port.md)),
128
+ so a Pool's hand-over re-attaches to each live run. An author's own key (`dispatch({ key })`) is not offered now; to
129
+ add it later breaks nothing. The key lives in the callee's blob (a Pool's `dispatchedBy.key`, else
130
+ `dispatchedBy.effect`), and the store copies it and the Workflow into columns of their own (`dispatch_effect`,
131
+ `dispatch_workflow`) at every save and indexes the pair, not an expression index over `json_extract`: SQLite's JSON
132
+ parser refuses a blob nested about a thousand deep, and such an index would fail that row's save and every later open
133
+ of the volume. A run restore marks `lost` keeps its row and its blob, so its key keeps answering; the sweep of rows
134
+ older than `runs.retain` ([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)) removes
135
+ the key with the row, and a re-send after it starts a new run: known, and not guarded. In-process the ledger is
136
+ consulted directly, through the same start under the same key, with the starts in flight held so two arrivals of one
137
+ key before the first save are one run. So a dispatch is idempotent whatever its author writes: its intent row says
138
+ `idempotent: true`, a restore that cannot tell whether its start arrived sends it again under its key with what is
139
+ left of the bound, the Instance Effector sends a start whose answer was lost again under its key with backoff until
140
+ the bound, and the Effector's rollout grace is never stretched by a dispatch's wait. Once the answer names the run,
141
+ the intent row carries it, the Effector's `running` state carries it on the wire, and a restore with it starts
142
+ nothing: it settles `started` at once, or resumes the wait. Without it a watcher double-starts a run after a restart,
143
+ which a human notices as two pull requests. What it does not guard: a door at a Kit version before this decision
144
+ ignores the header, so a re-send to it starts a second run, and a door that knows the key but not the key-only ask
145
+ reads one as a start with no body; neither the Effector nor the preflight can tell. A capability check is named and
146
+ deferred.
121
147
  - **`jr2 up` checks what it can see, and the severity follows who owns both sides.** For a same-Instance name the walk
122
148
  resolves it in the Assembly and REFUSES a miss, naming the fix: on the host, among the Machines and the Chart files,
123
149
  and a slashed name whose wrapper exists but whose Chart the host cannot see is a warning there, since a runtime Chart
@@ -172,7 +198,7 @@ composition and does not replace it. The two uses stay two mechanisms.
172
198
  - ADR-0049's "no sub-run" is restated, not reversed: a nested Machine is reached by `import`, never by name; a run that
173
199
  starts a run is a `dispatch`, by name, and the two are different uses. ADR-0067 gains the kind and a fourth Placement,
174
200
  `orchestrator`. ADR-0068's Instance Effector gains the `dispatch` kind, its stop route stops a dispatch's wait too,
175
- and its `running` state carries the run. ADR-0033's door gains an idempotency key.
201
+ and its `running` state carries the run. ADR-0033's door gains an idempotency key and the key-only ask.
176
202
  - The GLOSSARY's **Effect** gains "or starts a run", and `_Avoid_: sub-run, child run, nested run, spawned run` for the
177
203
  relation, which is **dispatched by**; its **Effector** and **Instance Effector** entries name the in-process exception
178
204
  and the new kind.
@@ -180,4 +206,4 @@ composition and does not replace it. The two uses stay two mechanisms.
180
206
  routing of both, without the key.
181
207
  - An Open body may lend a `dispatch` slot as it lends any Effect (`lends.effects`), so a Chart can start a run of a
182
208
  Workflow its wrapper chose; the Chart fills the body only.
183
- - Deferred, named: a start-only token; a depth ceiling; a capability check for a door that ignores the key.
209
+ - Deferred, named: a depth ceiling; a capability check for a door that ignores the key; an author's own key.
@@ -15,22 +15,27 @@ Next.js ships the same design: its docs as Markdown inside the `next` package, a
15
15
  - **The bundle.** `@jr2/orchestrator` ships `docs/`: every page of the sitemap, the generated Reference, the Glossary
16
16
  and the Decisions, as plain Markdown. No site-generator syntax reaches it. It is the main channel for an Assistant.
17
17
  Each page is at its slug, such as `concepts/gates.md`, and each ADR is at `reference/decisions/<file>`. An Assistant
18
- reads a page with its file tool, which cuts off a long file. So the Authoring surface is two parts:
19
- `reference/authoring-surface.md`, a contents page that gives each name its kind and the first sentence of its TSDoc,
20
- and `reference/authoring-surface/<anchor>.md`, one page for each export and for each type that a signature names.
21
- One page of all the names is about 200 KB, and the contents page is less than 25 KB. No line of the contents page or
22
- of an entry page is longer than 1,000 characters, so the type plumbing is a list, not a paragraph. When the contents
23
- page passes 25 KB, first make the long first sentences shorter; if it still passes, give each group of the contents
24
- page a page of its own, and make the contents page a list of the groups. The first sentence ends at a stop, not at a
25
- colon, because the words before a colon often tell nothing alone ("An invoke."). A link to an entry goes to its
26
- page. Each of the other generated Reference pages (CLI, config, machines, mechanism events, HTTP API) stays one
27
- page. Each is less than 30 KB, the size that one read of a file tool shows, with one exception: the HTTP API page is
28
- about 50 KB. It is one page because its routes come first and are less than 30 KB, so one read shows every route;
29
- the types of the bodies come after the routes, and an Assistant reads one of them with an offset or by its heading.
30
- When the routes pass 30 KB, give the types a page of their own. The alternative, a split of the HTTP API page now,
31
- is rejected because a route and the types it names are then on two pages, and one read shows all routes already. The
32
- Glossary and the Decisions are not generated, and this rule does not hold them: an ADR can be about 50 KB, and an
33
- Assistant reads the one ADR that a page links to.
18
+ reads a page with its file tool, which cuts off a long file. So the Authoring surface is three parts.
19
+ `reference/authoring-surface.md` is a contents page that gives each group of the root's `src/index.ts`, and the
20
+ group of the types that the signatures name, its description and its names.
21
+ `reference/authoring-surface/group-<title>.md` is one page for each group, which gives each name of the group its
22
+ kind and the first sentence of its TSDoc. `reference/authoring-surface/<anchor>.md` is one page for each export and
23
+ for each type that a signature names. The `group-` prefix keeps a group page apart from an entry page, and
24
+ `docs:build` fails when two pages have one path. One page of all the names is about 200 KB. The contents page and
25
+ each group page are less than 25 KB, and each entry page is less than 15 KB. On the contents page, a type that a
26
+ signature names is code with no link, because there are more than a hundred of them; its group page links each one.
27
+ No line of a contents, group or entry page is longer than 1,000 characters, so the contents page breaks a list of
28
+ names into lines, and the type plumbing is a list, not a paragraph. When a group page passes 25 KB, first make the
29
+ long first sentences shorter; if it still passes, divide that group into two groups. The first sentence ends at a
30
+ stop, not at a colon, because the words before a colon often tell nothing alone ("An invoke."). A link to an entry
31
+ goes to its page. Each of the other generated Reference pages (CLI, config, machines, Hook kinds, mechanism events,
32
+ HTTP API) stays one page. Each is less than 30 KB, the size that one read of a file tool shows, with one exception:
33
+ the HTTP API page is about 50 KB. It is one page because its routes come first and are less than 30 KB, so one read
34
+ shows every route; the types of the bodies come after the routes, and an Assistant reads one of them with an offset
35
+ or by its heading. When the routes pass 30 KB, give the types a page of their own. The alternative, a split of the
36
+ HTTP API page now, is rejected because a route and the types it names are then on two pages, and one read shows all
37
+ routes already. The Glossary and the Decisions are not generated, and this rule does not hold them: an ADR can be
38
+ about 50 KB, and an Assistant reads the one ADR that a page links to.
34
39
  - **The `AGENTS.md` block.** A compressed index of the bundle, and an instruction in five parts. jr2 is newer than
35
40
  your training data, so read the bundled docs before you write a Machine or config. Start with the guide "Write a
36
41
  workflow" and the contents page of the Authoring surface. Read one page with each call of the file tool. Run
@@ -63,21 +68,26 @@ Next.js ships the same design: its docs as Markdown inside the `next` package, a
63
68
  exists for it. A broken marker pair is reported, and `up` does not change the file.
64
69
  - **The Reference is generated from the code wherever the code holds the facts.**
65
70
 
66
- | Page | Source |
67
- | ------------------------------------------------------- | ------------------------------------------------------ |
68
- | Authoring surface: a contents page, and a page per name | TSDoc on the exports, exactly the root's list |
69
- | `@jr2/machines` | TSDoc on the exports |
70
- | `jr2.config.ts` | TSDoc on the `JR2Config` fields |
71
- | CLI | a structured command table, which also prints `--help` |
72
- | HTTP API | the route table in `http.ts`, made data |
73
- | Mechanism events | the code that defines them |
74
- | Glossary, Decisions | `GLOSSARY.md` and `docs/adr/`, rendered as they are |
71
+ | Page | Source |
72
+ | ------------------------------------------------------------------------- | -------------------------------------------------------------- |
73
+ | Authoring surface: a contents page, a page per group, and a page per name | TSDoc on the exports, exactly the root's list |
74
+ | `@jr2/machines` | TSDoc on the exports |
75
+ | `@jr2/orchestrator/hooks` | TSDoc on the exports of each module but `door`, one group each |
76
+ | `jr2.config.ts` | TSDoc on the `JR2Config` fields |
77
+ | CLI | a structured command table, which also prints `--help` |
78
+ | HTTP API | the route table in `http.ts`, made data |
79
+ | Mechanism events | the code that defines them |
80
+ | Glossary, Decisions | `GLOSSARY.md` and `docs/adr/`, rendered as they are |
75
81
 
76
82
  The TSDoc on an Authoring surface export is written for the author: what it does, what it refuses, what to read next,
77
83
  and one example. Maintainer reasoning goes in `//` comments in the body. The packages ship their `.ts` source, so an
78
84
  Assistant that follows an import into `node_modules` reads the same text the site shows. The Authoring surface is
79
85
  exactly what `@jr2/orchestrator` exports from its root: what the kit's packages share is internal even where it can be
80
86
  imported, and `@jr2/machines` is not part of it, because it ships Machines, not names to author with.
87
+ `@jr2/orchestrator/hooks` is not part of it either, because a Hook kind is a module of its own interface, which a kind
88
+ from another package imports too
89
+ ([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). Its page leaves out
90
+ `hook-kinds/door.ts`, which only the Orchestrator reads.
81
91
 
82
92
  - **`docs:build` assembles the bundle at pack time.** Its inputs are the hand-written pages in `docs/site/`, the
83
93
  generated Reference, `GLOSSARY.md`, and `docs/adr/`. It runs in `@jr2/orchestrator`'s `prepack` and before the site
@@ -106,8 +106,8 @@ send: { name: "r.md", worktree: "/work/app/fix" } // http with a file body
106
106
 
107
107
  ### Output and the walk
108
108
 
109
- - **Every Effect outcome is open.** `worktree()`, `push`, `command` and `http` each hold a stand-in member in their
110
- union, so that no author can write an exhaustive check. A later outcome is then no break.
109
+ - **Every Effect outcome is open.** `worktree()`, `push`, `command`, `http` and `mcp` each hold a stand-in member in
110
+ their union, so that no author can write an exhaustive check. A later outcome is then no break.
111
111
  - **The load-time walk refuses an Effect's `onDone` with no unguarded last arm.** A type cannot see a missing arm, and
112
112
  with no match a run stays in that state and shows no fault.
113
113
 
@@ -151,7 +151,8 @@ a pod, jr2 drops this lock and adds a `@model` claim that an Agent runs the fetc
151
151
  (ADR-0021).
152
152
  - **An Agent's commits on a side branch are not published**, and `found` says `held`. A warning field can follow after
153
153
  v1.
154
- - **`jr2 status <run>` shows each path and branch.** `jr2 ls` shows only Sandboxes, because it reads cluster labels.
154
+ - **`jr2 status <run>` shows each path and branch.** `jr2 workspace ls` shows only Sandboxes, because it reads cluster
155
+ labels.
155
156
 
156
157
  ## Example
157
158
 
@@ -18,8 +18,8 @@ code from `/work`.
18
18
 
19
19
  - **The boundary.** A credential that lets a request act for an Agent lives only where no Workspace code runs.
20
20
  **Workspace code** is each process whose program or input comes from `/work`: the Working tools, tests, builds, git
21
- (its hooks, its filters, fsmonitor, textconv and each repository-local config) and `command` Effects. The boundary
22
- needs no separate container for each Agent.
21
+ (its hooks, its filters, fsmonitor, textconv and each repository-local config), `command` Effects and stdio MCP
22
+ servers. The boundary needs no separate container for each Agent.
23
23
  - **One Harness container for each Sandbox.** It serves every workspace-ful Agent below the `workspace()`
24
24
  ([ADR-0005](0005-sandbox-pod-composition.md)). A Menu-only Agent has no Sandbox, and it runs on the Instance Harness,
25
25
  as before ([ADR-0031](0031-menu-only-agents-run-on-the-instance-harness.md)).
@@ -33,7 +33,10 @@ code from `/work`.
33
33
  ([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md),
34
34
  [ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)), because a smudge filter or a textconv
35
35
  in the shared `.git/config` runs in git's process;
36
- - each `command` Effect that is placed in the Harness, which is each `command` that names no secret.
36
+ - each `command` Effect that is placed in the Harness, which is each `command` that names no secret;
37
+ - each stdio MCP server of an Agent
38
+ ([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)),
39
+ which the Harness starts through the spawner, never with its MCP client's own spawn.
37
40
 
38
41
  So the trust boundary is inside the Harness container: the Harness process on one side, the domain on the other.
39
42
  [ADR-0077](0077-jr2-requires-landlock-on-every-sandbox-node.md) makes Landlock a requirement of jr2, so no path runs a
@@ -54,17 +57,33 @@ code from `/work`.
54
57
  to find what a rule allows: a link gets no rule (what it names gets its own rule where it is allowed, and the kernel
55
58
  checks the path that a link resolves to), and the confiner refuses to start when a rule's path has a link on it. A
56
59
  rule on a link would allow what the link names at each restart, and a confined process can change that.
60
+ - **A person's shell is a process of the domain.** `jr2 workspace shell`
61
+ ([ADR-0009](0009-cli-and-instance-interface.md)) is `kubectl exec -c harness` of a small client in `/opt/jr2`, which
62
+ starts as the Harness starts (`effector nocore --harness-env`). The client starts nothing itself. It asks the Harness,
63
+ on a Unix socket in `/run/jr2/shell`, for the shell or for the one command after `--`, and the Harness asks the
64
+ spawner. So the shell is in the one domain, with the domain's env (the Stand-ins that the Workspace grants), its umask
65
+ and git identity, and the Workspace's cwd. It sees, signals and traces the Agents' processes, as a later `bash` call
66
+ does: a person can stop a dev server that an Agent left. With a terminal, the spawner opens a pseudo-terminal for the
67
+ shell, as its controlling terminal in a session of its own. The client sends the keys, the terminal's size and the
68
+ hang-up, and the spawner closes the terminal when the shell exits. The output has flow control: while the socket to
69
+ the client is full, the Harness pauses the spawn, and the spawner reads none of its output until the resume. So the
70
+ terminal fills and the shell's writes block, as on a slow terminal, and a fast shell does not fill the memory of the
71
+ Harness that serves the Agents' Turns. `/run/jr2/shell` is an emptyDir of the Harness container alone, and the domain
72
+ can make no file under `/run/jr2`, so no process of the domain listens in the Harness's place. A process of the domain
73
+ can connect to the socket, because Landlock does not control a connect. So the client proves that it is not one: its
74
+ first frame carries a digest of the control Stand-in, which it reads from the file and which the domain cannot read.
75
+ The Stand-in itself never goes on the socket. Without the proof, the Harness starts nothing.
57
76
  - **The domain can do what the container's uid can do, except reach jr2's secrets.** The rules give every right on every
58
77
  path, with one exception: `/run/jr2`. There the domain can list the names in each folder, and do nothing else.
59
78
  Landlock only narrows the Unix permissions, so a confined program can do everything that the uid can do without
60
79
  Landlock, except:
61
80
  - reach the contents of `/run/jr2`: read, write, truncate, make, remove, move or link a file or a folder there.
62
81
  `/run/jr2` holds the control Stand-in, each held secret's Stand-in, the folder of the Custodian's control socket,
63
- and the Harness's conversations. Every path that jr2 holds for the Harness alone is under `/run/jr2`. Landlock
64
- allows a path only by its rules, so the confiner allows every sibling of `/run/jr2` and of each folder above it, one
65
- level at a time. It also gives the root the right to list names on its whole tree, so `ls /`, `ls /run` and `find /`
66
- work as they do outside jr2. That right lists the names under `/run/jr2` too: a held secret's name, a conversation's
67
- file name. These names are not secrets.
82
+ the folder of the shell's socket, and the Harness's conversations. Every path that jr2 holds for the Harness alone
83
+ is under `/run/jr2`. Landlock allows a path only by its rules, so the confiner allows every sibling of `/run/jr2`
84
+ and of each folder above it, one level at a time. It also gives the root the right to list names on its whole tree,
85
+ so `ls /`, `ls /run` and `find /` work as they do outside jr2. That right lists the names under `/run/jr2` too: a
86
+ held secret's name, a conversation's file name. These names are not secrets.
68
87
  - trace the Harness process, or read its `/proc/<pid>/environ` or `/proc/<pid>/mem`. A confined process cannot ptrace
69
88
  a process outside its domain.
70
89
  - at Landlock ABI 6 or later, connect to an abstract Unix socket that a process outside the domain made. The
@@ -167,8 +186,9 @@ code from `/work`.
167
186
  the security boundary: the boundary is that only the Harness holds the credential.
168
187
  - **Two tokens, each checked on its own routes.** This is defence in depth:
169
188
  - The **control token** is what the Custodian puts in place of the secret control Stand-in. It is valid only on the
170
- agent routes (`/agents/<iid>/surface`, `/agents/<iid>/events`, `/agents/<iid>/deliveries/<id>`, and the route on
171
- which the Harness acknowledges a notice), and it carries the stated slot.
189
+ agent routes (`/agents/<iid>/surface`, `/agents/<iid>/events`, `/agents/<iid>/deliveries/<id>`, the route on which
190
+ the Harness acknowledges a notice, and `/agents/<iid>/conversation`, on which it sends the entries of the
191
+ conversation, [ADR-0023](0023-the-harness-prints-the-conversation.md)), and it carries the stated slot.
172
192
  - The **fetch token** is what the Custodian puts in place of the public Sandbox Stand-in that `jr2-upload-pack` holds
173
193
  as a constant. It is valid only on `POST /sandboxes/<sandbox>/fetch`
174
194
  ([ADR-0053](0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md)). The Custodian
@@ -206,6 +226,13 @@ code from `/work`.
206
226
 
207
227
  ## Considered options
208
228
 
229
+ - **A raw `kubectl exec -c harness` shell (the old `jr2 ssh`).** Rejected: it runs outside the Landlock domain and can
230
+ read `/run/jr2`, so a person's shell, and any code that the person runs in it, reaches the control Stand-in.
231
+ `jr2 workspace shell` is a process of the spawner's domain ([ADR-0009](0009-cli-and-instance-interface.md)).
232
+ - **A person's shell in a sibling domain with the same rules**, which the client makes again with the confiner, so the
233
+ spawner needs no pseudo-terminal and the Harness needs no socket. Rejected: the shell reaches what an Agent's `bash`
234
+ reaches and nothing more, but it is not in the Agents' domain. It cannot trace their processes, and at ABI 6 or later
235
+ it cannot signal them, so a person cannot stop a runaway dev server, which is a usual reason to open a shell.
209
236
  - **One Harness container for each agent slot, each with its own secret Stand-in.** Rejected. Kubernetes isolates one
210
237
  container from another, but all the slots share the writable `/work`. Code that one Agent plants there runs in a
211
238
  sibling's container when the sibling's `bash`, git or tests run, or when a `command` Effect runs there, and that code