@jr2/orchestrator 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/docs/agents-block.md +2 -2
  2. package/docs/concepts/agents-and-turns.md +56 -20
  3. package/docs/concepts/architecture.md +30 -17
  4. package/docs/concepts/charts.md +6 -3
  5. package/docs/concepts/converge.md +3 -0
  6. package/docs/concepts/effects.md +86 -31
  7. package/docs/concepts/machines-and-workflows.md +9 -0
  8. package/docs/concepts/pools-and-sources.md +6 -6
  9. package/docs/concepts/secrets-and-trust.md +126 -42
  10. package/docs/concepts/the-menu.md +3 -0
  11. package/docs/concepts/workspaces.md +129 -51
  12. package/docs/get-started/an-agent-in-a-sandbox.md +21 -19
  13. package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
  14. package/docs/get-started/install.md +4 -0
  15. package/docs/guides/add-tools-to-the-sandbox.md +17 -4
  16. package/docs/guides/bind-open-parts.md +5 -4
  17. package/docs/guides/cut-a-worktree.md +560 -0
  18. package/docs/guides/fix-a-failed-converge.md +1 -1
  19. package/docs/guides/let-an-agent-write-a-chart.md +5 -1
  20. package/docs/guides/look-inside-a-sandbox.md +18 -15
  21. package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
  22. package/docs/guides/push-and-sign-a-branch.md +67 -21
  23. package/docs/guides/run-a-command-or-call-a-url.md +84 -41
  24. package/docs/guides/run-one-worker-per-item.md +19 -18
  25. package/docs/guides/set-an-agents-access-and-dials.md +67 -22
  26. package/docs/guides/size-a-workspace.md +19 -6
  27. package/docs/guides/use-a-hosted-provider.md +6 -0
  28. package/docs/guides/watch-a-run.md +47 -24
  29. package/docs/guides/write-a-workflow.md +1 -1
  30. package/docs/llms.txt +33 -12
  31. package/docs/reference/authoring-surface/agent.md +34 -11
  32. package/docs/reference/authoring-surface/answer.md +19 -3
  33. package/docs/reference/authoring-surface/arg.md +27 -5
  34. package/docs/reference/authoring-surface/branchname.md +15 -0
  35. package/docs/reference/authoring-surface/command.md +36 -21
  36. package/docs/reference/authoring-surface/fail.md +17 -4
  37. package/docs/reference/authoring-surface/file.md +36 -16
  38. package/docs/reference/authoring-surface/http.md +108 -8
  39. package/docs/reference/authoring-surface/isready.md +14 -0
  40. package/docs/reference/authoring-surface/jr2setup.md +1 -1
  41. package/docs/reference/authoring-surface/open.md +36 -5
  42. package/docs/reference/authoring-surface/paramsof.md +22 -4
  43. package/docs/reference/authoring-surface/pool.md +4 -4
  44. package/docs/reference/authoring-surface/push.md +30 -12
  45. package/docs/reference/authoring-surface/readyof.md +21 -0
  46. package/docs/reference/authoring-surface/secret.md +25 -4
  47. package/docs/reference/authoring-surface/source.md +3 -3
  48. package/docs/reference/authoring-surface/type-agentdefinition.md +2 -2
  49. package/docs/reference/authoring-surface/type-agentslots.md +4 -1
  50. package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
  51. package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
  52. package/docs/reference/authoring-surface/type-bindactors.md +14 -0
  53. package/docs/reference/authoring-surface/type-binding.md +5 -6
  54. package/docs/reference/authoring-surface/type-commandinput.md +4 -3
  55. package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
  56. package/docs/reference/authoring-surface/type-commandoutput.md +22 -4
  57. package/docs/reference/authoring-surface/type-customize.md +4 -2
  58. package/docs/reference/authoring-surface/type-dispatchoutput.md +8 -1
  59. package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
  60. package/docs/reference/authoring-surface/type-filebody.md +4 -8
  61. package/docs/reference/authoring-surface/type-framepath.md +14 -0
  62. package/docs/reference/authoring-surface/type-hostinjectedinput.md +2 -4
  63. package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
  64. package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
  65. package/docs/reference/authoring-surface/type-httpoutput.md +7 -1
  66. package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
  67. package/docs/reference/authoring-surface/type-namestart.md +77 -0
  68. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
  69. package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
  70. package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
  71. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
  72. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
  73. package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
  74. package/docs/reference/authoring-surface/type-poolspec.md +2 -2
  75. package/docs/reference/authoring-surface/type-pushinput.md +22 -3
  76. package/docs/reference/authoring-surface/type-pushoutput.md +32 -7
  77. package/docs/reference/authoring-surface/type-reposlot.md +5 -5
  78. package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
  79. package/docs/reference/authoring-surface/type-slothandles.md +5 -13
  80. package/docs/reference/authoring-surface/type-sourcespec.md +10 -10
  81. package/docs/reference/authoring-surface/type-workspaced.md +29 -8
  82. package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
  83. package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
  84. package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
  85. package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
  86. package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
  87. package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
  88. package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
  89. package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
  90. package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
  91. package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
  92. package/docs/reference/authoring-surface/workspace.md +65 -37
  93. package/docs/reference/authoring-surface/worktree.md +71 -0
  94. package/docs/reference/authoring-surface.md +30 -10
  95. package/docs/reference/cli.md +1 -1
  96. package/docs/reference/config.md +13 -4
  97. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
  98. package/docs/reference/decisions/0002-duplex-streaming-actor.md +17 -10
  99. package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
  100. package/docs/reference/decisions/0005-sandbox-pod-composition.md +29 -19
  101. package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
  102. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +30 -30
  103. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +35 -17
  104. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +8 -2
  105. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
  106. package/docs/reference/decisions/0017-pool-over-a-source-port.md +6 -4
  107. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +21 -9
  108. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +2 -1
  109. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +8 -9
  110. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +62 -65
  111. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +9 -1
  112. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +13 -12
  113. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +4 -1
  114. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +18 -19
  115. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +8 -8
  116. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +4 -4
  117. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
  118. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
  119. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
  120. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +24 -24
  121. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +5 -3
  122. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +110 -67
  123. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +101 -74
  124. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +5 -6
  125. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +2 -0
  126. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +181 -153
  127. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +74 -63
  128. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +31 -20
  129. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
  130. package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +295 -0
  131. package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
  132. package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
  133. package/docs/reference/decisions.md +3 -0
  134. package/docs/reference/glossary.md +51 -12
  135. package/docs/reference/http-api.md +45 -7
  136. package/docs/reference/machines.md +0 -1
  137. package/docs/why.md +5 -4
  138. package/package.json +3 -3
  139. package/src/actor.ts +161 -35
  140. package/src/agent.ts +6 -6
  141. package/src/ambient.ts +3 -5
  142. package/src/chart.ts +186 -19
  143. package/src/charts.ts +9 -2
  144. package/src/config.ts +16 -6
  145. package/src/custodian.lua +17 -5
  146. package/src/custodian.ts +251 -77
  147. package/src/customize.ts +11 -3
  148. package/src/effect-checks.ts +50 -3
  149. package/src/effect.ts +343 -127
  150. package/src/effector-wire.ts +6 -3
  151. package/src/fail.ts +17 -4
  152. package/src/harness-client.ts +74 -31
  153. package/src/harness-env.ts +33 -0
  154. package/src/held-secrets.ts +125 -16
  155. package/src/holds.ts +3 -0
  156. package/src/http-effect.ts +88 -32
  157. package/src/http.ts +56 -17
  158. package/src/index.ts +15 -1
  159. package/src/internal.ts +1 -0
  160. package/src/kube-client.ts +6 -3
  161. package/src/machine-doc.ts +3 -4
  162. package/src/model-keys.ts +24 -0
  163. package/src/names.ts +19 -6
  164. package/src/notices.ts +443 -54
  165. package/src/open-body.ts +85 -19
  166. package/src/open.ts +36 -5
  167. package/src/parts.ts +40 -28
  168. package/src/party.ts +33 -0
  169. package/src/pick.ts +41 -7
  170. package/src/pool.ts +44 -23
  171. package/src/registration.ts +53 -11
  172. package/src/repo-identity.ts +7 -3
  173. package/src/repos.ts +20 -2
  174. package/src/run-host.ts +106 -20
  175. package/src/sandbox-effect.ts +217 -66
  176. package/src/sandbox-kube.ts +63 -16
  177. package/src/secret-placement.ts +9 -2
  178. package/src/setup.ts +86 -6
  179. package/src/tokens.ts +107 -32
  180. package/src/vocabulary.ts +23 -4
  181. package/src/wire.ts +10 -10
  182. package/src/workspace.ts +264 -253
  183. package/src/worktree.ts +249 -0
  184. package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
  185. package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
@@ -0,0 +1,79 @@
1
+ # jr2 requires Landlock on every Sandbox node
2
+
3
+ Two boundaries of jr2 are Landlock domains. [ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)
4
+ confines the child of a credentialed `command` in the Effector, so that the Agent's code cannot read the Effector's key
5
+ or a secret that the command does not name.
6
+ [ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md) confines each process that the
7
+ Harness starts in `/work`, so that no Workspace code can read the control credential and act as an Agent. On a node with
8
+ no Landlock, neither boundary exists. A path that runs there with less confinement is a second jr2, with a weaker
9
+ boundary, to support and to test.
10
+
11
+ ## Decision
12
+
13
+ - **Landlock is a requirement of jr2 on every Sandbox node.** It holds where Workspace code runs, which is also where
14
+ the check reaches. No component has an unconfined branch, anywhere.
15
+ - **The Repo cache agent detects it, on each Sandbox node.** The cache agent's DaemonSet has one pod on each Sandbox
16
+ node, by the same `nodeSelector` and `tolerations` as the Sandboxes
17
+ ([ADR-0052](0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md)). The agent probes Landlock
18
+ on its node. On a node without it, the agent is NotReady, and its status gives the reason.
19
+ - **`jr2 up` refuses to converge while a Sandbox node lacks Landlock.** It reads the DaemonSet's pods (one list call),
20
+ and it names each Sandbox node whose cache agent reports no Landlock
21
+ ([ADR-0019](0019-one-converging-command-against-the-current-context.md)). The admin keeps a bad node out of the set
22
+ with `sandbox.nodeSelector` or a taint, as ADR-0052 says. There is still no jr2 node label.
23
+ - **The refusal is an ordinary converge failure** (ADR-0019). It comes within the cache agent's rollout wait, which
24
+ `jr2 up` already makes. It names each Sandbox node without Landlock, the probe's reason, and the fix: keep the node
25
+ out with `sandbox.nodeSelector` or a taint (ADR-0052), or enable Landlock in its kernel. A run of `jr2 up` after the
26
+ fix converges.
27
+ - **Each confining component probes at its start, and refuses to serve without it.** These are the Harness of a Sandbox
28
+ and the Sandbox Effector. The probe is `landlock_create_ruleset` with `LANDLOCK_CREATE_RULESET_VERSION`. This is the
29
+ backstop for a node that joins after the converge.
30
+ - **No component keeps a path for a node that cannot confine a child.** ADR-0068's Effector refuses to start on such a
31
+ node, whatever its Workspace holds.
32
+ - **The minimum ABI is 3.** Under ABI 1, a confined process can never move or link a file into another folder, whatever
33
+ the rules allow, and package managers, git and `mv` do that. ABI 2 adds the `REFER` right, so the domain can do it
34
+ (ADR-0076). Under ABI 2, Landlock does not handle `truncate(2)`, so a confined process can truncate a file that the
35
+ rules let it do nothing else to: a conversation under `/run/jr2`, which the Harness rebuilds an Agent's record from.
36
+ ABI 3 adds the `TRUNCATE` right, and the confiner always handles it. The confiner uses the scopes of abstract Unix
37
+ sockets and of signals (ABI 6) when the kernel offers them. Each probe refuses a kernel below ABI 3, with the ABI that
38
+ it found.
39
+ - **The components that probe.** The cache agent probes as the detector, though it confines nothing. The Harness of a
40
+ Sandbox and the Sandbox Effector probe because they confine. The Instance Harness and the Instance Effector confine
41
+ nothing: a Menu-only Agent has no Working tools ([ADR-0031](0031-menu-only-agents-run-on-the-instance-harness.md)),
42
+ and the Instance Effector runs no `command`. They do not probe.
43
+
44
+ ## Considered options
45
+
46
+ - **A minimum of ABI 1.** Rejected: under it no confined process can move a file into another folder, which stops normal
47
+ work.
48
+ - **A minimum of ABI 2.** Rejected: under it the domain can truncate a file under `/run/jr2`. One Agent's code can then
49
+ erase or roll back another Agent's conversation, and below ABI 6 it can end the Harness so that the Harness rebuilds
50
+ from the cut file. It admits Linux 5.19 to 6.1 (Debian 12's stock kernel among them), for a boundary with a hole.
51
+ - **Refuse only a Sandbox with more than one Agent on a node without Landlock.** Rejected: it keeps a less secure
52
+ branch, which jr2 must then support and test.
53
+ - **Run unconfined, with a warning.** Rejected: Workspace code can then read the control credential, and a warning does
54
+ not stop it.
55
+ - **A check pod on each node at each `jr2 up`.** Rejected: it starts one pod for each node on every converge, and it
56
+ misses a node that joins later.
57
+ - **One check pod, with the start-up probe as the backstop.** Rejected: a bad node shows only when a run lands there and
58
+ fails.
59
+ - **jr2 labels the nodes that have Landlock, and a Sandbox requires the label.** Rejected: it reverses ADR-0052, and it
60
+ needs a cluster-wide permission to patch Nodes.
61
+
62
+ ## Consequences
63
+
64
+ - **The kernels that this excludes.** Landlock ABI 3 is in Linux 6.2 and later. A node with an older kernel cannot run
65
+ jr2: for example RHEL 8 (4.18), Debian 11 (5.10), Debian 12 on its stock kernel (6.1), Ubuntu 20.04 and 22.04 on their
66
+ first kernels (5.4, 5.15), and Amazon Linux 2 (4.14 or 5.10). A distribution kernel can carry a backport: the probe,
67
+ not the version, decides. A newer kernel must also enable Landlock: its list of security modules (`CONFIG_LSM`, or the
68
+ `lsm=` boot parameter) must name `landlock`. On a node, `/sys/kernel/security/lsm` lists the modules that run.
69
+ Measured from a hardened pod with no capabilities: kind on WSL2 (6.6) offers ABI 3; the home cluster's arm64 nodes
70
+ (6.8), ABI 4; spark (6.11, arm64), ABI 5.
71
+ - **kind on a host inherits the host's kernel.** A kind node is a container, so the kernel of the host decides. On macOS
72
+ or Windows, Docker runs the nodes in a virtual machine, and the kernel of that machine decides.
73
+ - **A mixed cluster.** `jr2 up` names each Sandbox node without Landlock and refuses until the admin keeps it out of the
74
+ set. A node that joins after the converge shows as a NotReady cache agent. A Sandbox placed on it fails at its start,
75
+ because its Harness refuses to serve.
76
+ - **The check runs when a Machine uses a Sandbox.** `jr2 up` converges the cache agent then
77
+ ([ADR-0051](0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md)). With no Sandbox, no node needs
78
+ Landlock, and there is nothing to check.
79
+ - **The docs list the requirement** where they list what jr2 needs from a cluster and from a host.
@@ -81,3 +81,6 @@ Each architecture decision record (ADR) says what jr2 decided, why, and what it
81
81
  | 0072 | [The docs ship inside the Kit, and an Instance's `AGENTS.md` indexes them](decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md) |
82
82
  | 0073 | [A Machine fails a run with `fail()`, and an uncaught Failure ends it `failed`](decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md) |
83
83
  | 0074 | [An unrouted mechanism event is refused at load and faults the run](decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md) |
84
+ | 0075 | [A Machine cuts a Worktree, and the Workspace only clones](decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md) |
85
+ | 0076 | [An Agent's control credential lives only where no Workspace code runs](decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md) |
86
+ | 0077 | [jr2 requires Landlock on every Sandbox node](decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md) |
@@ -213,7 +213,16 @@ _Avoid_: humanGate (humans are one caller among many), approval (one possible ev
213
213
  **Sender**:
214
214
  The party that delivered an event: an Agent or a Gate. Only the Orchestrator writes the Sender, on each event that a
215
215
  party delivers, and the Machine's own events have none.
216
- _Avoid_: origin, caller (the human or system behind a Gate)
216
+ _Avoid_: origin, Caller (the holder of the credential behind a Gate's answer)
217
+
218
+ <a id="caller"></a>
219
+
220
+ **Caller**:
221
+ The holder of a credential that acts on an Instance from outside its Machines, such as a person, a CI job or a bridge
222
+ to a chat service: it starts a run, answers a Gate or cancels a run. A Caller can act **on behalf of** another party,
223
+ but that is the Caller's claim, which jr2 does not check.
224
+ _Avoid_: principal (it brings policies and roles that an Instance does not have), user (a CI job is a Caller), actor
225
+ (xstate's word), Sender (a party inside the run)
217
226
 
218
227
  <a id="emit"></a>
219
228
 
@@ -414,7 +423,8 @@ re-prompt of an open Turn)
414
423
 
415
424
  **Working tools**:
416
425
  The file and shell tools that the Harness runs in its own container: what an Agent may do, as its Menu is what it may
417
- say. The Agent's access to the Workspace decides which of them it gets, and a Menu-only Agent gets none.
426
+ say. Every program that they run, and their file I/O, is confined, so it cannot reach what the Harness itself holds. The Agent's access to
427
+ the Workspace decides which of them it gets, and a Menu-only Agent gets none.
418
428
  _Avoid_: tools (unqualified), sandbox tools
419
429
 
420
430
  ### Sandboxes, Harnesses and secrets
@@ -463,17 +473,37 @@ firewall
463
473
 
464
474
  **Held secret**:
465
475
  A credential that the Harness uses through its pod's Custodian but never holds: a model API key above all, and the
466
- Sandbox token. The Harness holds only its Stand-in, so the Agent cannot take the secret out of the pod, but it can spend
467
- it through the Custodian while the pod lives.
476
+ Sandbox tokens. The Harness holds only its Stand-in, so no Agent can take the secret out of the pod. Workspace code can
477
+ spend it through the Custodian, while the pod lives, only when its Workspace grants that secret.
468
478
  _Avoid_: masked secret, injected secret, proxied key, secret (unqualified)
469
479
 
470
480
  <a id="stand-in"></a>
471
481
 
472
482
  **Stand-in**:
473
- The public value that the Harness holds in place of a Held secret. It means something only to the Custodian, and only
474
- toward a host that the secret is bound to; sent anywhere else, it arrives as it left.
483
+ The value that the Harness holds in place of a Held secret. It means something only to the Custodian, and only toward a
484
+ host or a route that the secret is bound to; sent anywhere else, it arrives as it left. It is secret, and only the
485
+ Harness itself holds it: Workspace code gets a Stand-in only for a secret that its Workspace grants, and never the one
486
+ for the control token. The one public Stand-in is the fetch token's, which can only ask for a fetch.
475
487
  _Avoid_: placeholder (an Open part's word), dummy key, fake key, token, mask
476
488
 
489
+ <a id="sandbox-token"></a>
490
+
491
+ **Sandbox token**:
492
+ A credential that jr2 makes for one Sandbox, which its Custodian alone holds. There are two. The **control token** lets
493
+ the Harness read each Agent's Menu and deliver its picks, for the Agent that the Harness names. The **fetch token** lets
494
+ a process in the pod ask for a fetch of a Repo that the Sandbox holds. Each one is good only for its own work and in its
495
+ own Sandbox, and neither one reaches a Gate.
496
+ _Avoid_: pod token, agent token, slot token, Instance token (the Caller's credential)
497
+
498
+ <a id="workspace-code"></a>
499
+
500
+ **Workspace code**:
501
+ Each process in a Sandbox whose program or input comes from the Workspace's shared files: what an Agent's Working tools
502
+ run, tests and builds, git and what a repository's own config makes it run, and a command that a Machine runs there. Any
503
+ Agent can run what another Agent wrote, so jr2 trusts none of it, and no credential that acts for an Agent is where
504
+ Workspace code runs.
505
+ _Avoid_: agent code (any Agent can run another's), user code, untrusted code (unqualified), tool process
506
+
477
507
  <a id="check-pod"></a>
478
508
 
479
509
  **Check pod**:
@@ -629,20 +659,29 @@ _Avoid_: role, alias, repo name, workdir, primary repo
629
659
  <a id="binding"></a>
630
660
 
631
661
  **Binding**:
632
- What a Repo Slot resolves to: a Repo's url and an optional base from which the Worktree is cut. With no base, the
633
- Worktree is cut from the Repo's own default branch.
662
+ What a Repo Slot resolves to: a Repo's url and an optional base. The Sandbox's own clone stays on that base, and a new
663
+ branch starts from it. With no base, both use the Repo's own default branch.
634
664
  _Avoid_: config, catalog entry
635
665
 
636
666
  <a id="project-layout"></a>
637
667
 
638
668
  **Project layout**:
639
669
  The convention for the checkouts in a Sandbox: under each Repo Slot, the Sandbox's own clone of the Repo, with the
640
- Worktrees beside it. The node's cache of the Repo is not part of the layout.
670
+ Worktrees beside it. An Agent reads the clone and works in a Worktree. The node's cache of the Repo is not part of the
671
+ layout.
641
672
  _Avoid_: directory structure, repo tree
642
673
 
643
674
  <a id="worktree"></a>
644
675
 
645
676
  **Worktree**:
646
- A git worktree of a Repo Slot for the one branch that its Workspace works on, beside the Sandbox's own clone. Its work
647
- is sequential commits on that branch, not separate worktrees; the Machine, not jr2, chooses the branch and its scope.
648
- _Avoid_: per-task worktree, task branch
677
+ A git worktree of a Repo Slot, in a folder that a Machine names, beside the Sandbox's own clone. It holds one branch, or
678
+ one commit with no branch. The Machine, not jr2, chooses how many Worktrees it cuts, their branches, and which Agents
679
+ work in them.
680
+ _Avoid_: checkout, task branch
681
+
682
+ <a id="cut"></a>
683
+
684
+ **Cut**:
685
+ An Effect's act that makes a Worktree, or reads the one that a cut already made at that path. A cut that cannot happen
686
+ changes nothing.
687
+ _Avoid_: attach (the Workspace's step, which only clones), provision
@@ -178,8 +178,8 @@ Any token jr2 minted: the Instance token, or a Sandbox token. A Sandbox token re
178
178
 
179
179
  ### `GET /agents/:instanceId/surface`
180
180
 
181
- - Summary: An Agent's Menu and this Turn's Allowed picks. A Sandbox token reads only its own Sandbox's.
182
- - Response: [`AgentSurfaceView`](#type-agentsurfaceview)
181
+ - Summary: An Agent's Menu, this Turn's Allowed picks and the conversation's pending notices. A control token reads only its own pod's, for the slot it states.
182
+ - Response: [`AgentSurfaceView`](#type-agentsurfaceview) & { notices: Array<{ id, notice }> }
183
183
  - Stream: no
184
184
  - Decisions: [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md), [ADR-0029](decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)
185
185
 
@@ -202,11 +202,21 @@ Any token jr2 minted: the Instance token, or a Sandbox token. A Sandbox token re
202
202
  - Stream: no
203
203
  - Decisions: [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md), [ADR-0024](decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)
204
204
 
205
+ <a id="post-agents-instanceid-notices"></a>
206
+
207
+ ### `POST /agents/:instanceId/notices`
208
+
209
+ - Summary: Acknowledge the notices a step boundary appended to the Turn: they are heard in its conversation.
210
+ - Body: { heard: string[] }
211
+ - Response: { heard: number }
212
+ - Stream: no
213
+ - Decisions: [ADR-0062](decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md), [ADR-0076](decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)
214
+
205
215
  <a id="post-sandboxes-name-fetch"></a>
206
216
 
207
217
  ### `POST /sandboxes/:name/fetch`
208
218
 
209
- - Summary: Ask the node cache to fetch one Repo that this Sandbox mounts. A Sandbox token asks only for its own.
219
+ - Summary: Ask the node cache to fetch one Repo that this Sandbox mounts. A fetch token asks only for its own.
210
220
  - Body: { identity }
211
221
  - Response: [`FetchAnswer`](#type-fetchanswer): { fetched } | { stale, asOf }
212
222
  - Stream: no
@@ -498,6 +508,7 @@ type RunStatus = RunRecord & {
498
508
  chartHashes?: string[];
499
509
  output?: unknown;
500
510
  dispatched?: Dispatched[];
511
+ worktrees?: RunWorktree[];
501
512
  };
502
513
  ```
503
514
 
@@ -554,6 +565,12 @@ A run's current observable state. `fault` carries the error message when status
554
565
  The runs this run's `dispatch` Effects started, oldest first ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)): the chain's other
555
566
  direction, read off the caller's own ledger rows. A foreign dispatch's run is a run of the
556
567
  Orchestrator its target names. Absent when it started none.
568
+ - `worktrees` (optional): `RunWorktree[]`
569
+
570
+ The Worktrees this run's cuts made, oldest first ([ADR-0075](decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)), read off its ledger's settled
571
+ `ready` rows, so an Agent's `git switch` does not change them. A re-cut of one path and branch
572
+ is one entry. Behind the token, like `context`: a branch name is the workflow's data. Absent
573
+ when the run cut none.
557
574
 
558
575
  <a id="type-repostatus"></a>
559
576
 
@@ -632,6 +649,7 @@ type AgentSurfaceView = {
632
649
  instanceId: string;
633
650
  runId: string;
634
651
  sandbox?: string;
652
+ slot?: string;
635
653
  menu: SurfaceEvent[];
636
654
  allowed: string[];
637
655
  };
@@ -655,6 +673,10 @@ sandbox), and serving it lets a caller fail loudly on a surface that is not its
655
673
  - `instanceId`: `string`
656
674
  - `runId`: `string`
657
675
  - `sandbox` (optional): `string`
676
+ - `slot` (optional): `string`
677
+
678
+ The slot this surface belongs to ([ADR-0076](decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)): its Agent's actor path and agent name — the slot
679
+ the Harness must state to speak here. Absent on a registration no pod hosts.
658
680
  - `menu`: `SurfaceEvent[]`
659
681
  - `allowed`: `string[]`
660
682
 
@@ -951,6 +973,23 @@ ledger row was written.
951
973
  - `run`: `string`
952
974
  - `at`: `string`
953
975
 
976
+ <a id="type-runworktree"></a>
977
+
978
+ ### `RunWorktree`
979
+
980
+ ```ts
981
+ type RunWorktree = { slot: string; path: string; branch: string | null };
982
+ ```
983
+
984
+ One Worktree a `worktree()` Effect of this run cut ([ADR-0075](decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)): its Repo Slot, its path, and the
985
+ branch of the cut. `branch` is `null` for a detached Worktree.
986
+
987
+ **Fields**
988
+
989
+ - `slot`: `string`
990
+ - `path`: `string`
991
+ - `branch`: `string | null`
992
+
954
993
  <a id="type-reponodestate"></a>
955
994
 
956
995
  ### `RepoNodeState`
@@ -1229,7 +1268,7 @@ How the party that sends an event gets its answer.
1229
1268
 
1230
1269
  ```ts
1231
1270
  type EffectDoc =
1232
- | { kind: "command"; argv: string[]; timeoutMs: number; slot?: string; secrets?: string[]; pin?: true }
1271
+ | { kind: "command"; argv: string[]; timeoutMs: number; secrets?: string[]; pin?: true }
1233
1272
  | {
1234
1273
  kind: "http";
1235
1274
  method: string;
@@ -1244,16 +1283,15 @@ type EffectDoc =
1244
1283
  ```
1245
1284
 
1246
1285
  An Effect slot's parts, as the doc carries them ([ADR-0067](decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)): a command's argv (an argv slot reads
1247
- `<name>`), its timeout, and the Repo Slot whose Worktree is its cwd, when it names one.
1286
+ `<name>`) and its timeout. Its input names its Worktree, so the doc does not ([ADR-0075](decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)).
1248
1287
 
1249
1288
  **Forms**: it is one of these objects.
1250
1289
 
1251
- - Form 1: `kind`, `argv`, `timeoutMs`, `slot`, `secrets`, `pin`
1290
+ - Form 1: `kind`, `argv`, `timeoutMs`, `secrets`, `pin`
1252
1291
 
1253
1292
  - `kind`: `"command"`
1254
1293
  - `argv`: `string[]`
1255
1294
  - `timeoutMs`: `number`
1256
- - `slot` (optional): `string`
1257
1295
  - `secrets` (optional): `string[]`
1258
1296
  - `pin` (optional): `true`
1259
1297
  - Form 2: `kind`, `method`, `target`, `secrets`, `timeoutMs`, `idempotent`, `file`
@@ -119,7 +119,6 @@ import { body, type TaskInput } from "@jr2/machines";
119
119
 
120
120
  const fixed = workspace(body, {
121
121
  repos: { app: { url: "https://github.com/you/app.git" } },
122
- spec: ({ input }: { input: TaskInput }) => ({ branch: input.branch ?? "jr2/fix" }),
123
122
  });
124
123
 
125
124
  export const machine = customize(fixed, {
package/docs/why.md CHANGED
@@ -115,8 +115,8 @@ It talks to the model, and it runs the tools of the Agent. The Agent can run eac
115
115
  Agent that needs only its Menu has no Sandbox. Its Turns run on the
116
116
  [Instance Harness](reference/glossary.md#instance-harness).
117
117
 
118
- The Harness container holds no credential, so a command that an Agent runs cannot read one. jr2 keeps each credential in
119
- one of the two containers beside it:
118
+ The Harness runs each command of an Agent confined with Landlock, so the command cannot read a credential, and it cannot
119
+ send a pick for a different Agent. jr2 keeps each credential in one of the two containers beside the Harness:
120
120
 
121
121
  - The [Custodian](reference/glossary.md#custodian) holds each [Held secret](reference/glossary.md#held-secret): a key
122
122
  that the Agents can use, but cannot read. The Harness holds only a [Stand-in](reference/glossary.md#stand-in) for it.
@@ -192,8 +192,9 @@ These steps follow one run of the example Machine in the diagram. You write a Ma
192
192
  1. **You start a run.** You run `jr2 run feature`, with the ticket as the input of the run. The Machine starts in its
193
193
  first state, `code`.
194
194
  2. **The coder works in a Sandbox.** The state `code` is in a Workspace, so jr2 makes a Sandbox for it. The Sandbox
195
- holds a [Worktree](reference/glossary.md#worktree) of your Repo, on the branch of the run. The coder edits the files,
196
- runs the tests and commits. Then it picks `finish`, and the Machine moves to `review`.
195
+ holds a clone of your Repo, and the Machine cuts a [Worktree](reference/glossary.md#worktree) on the branch of the
196
+ run. The coder edits the files, runs the tests and commits. Then it picks `finish`, and the Machine moves to
197
+ `review`.
197
198
  3. **The reviewer gets a Turn.** The state `review` gives the reviewer a prompt and its Menu: `approved` and `changes`.
198
199
  The reviewer reads the change in the same Sandbox.
199
200
  4. **The reviewer picks.** If it picks `approved`, the Machine moves to `approval`. If it picks `changes`, the Machine
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jr2/orchestrator",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "The jr2 Orchestrator: xstate runtime, HTTP API and Console, and the kit pieces a Workflow imports.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -42,11 +42,11 @@
42
42
  "ts-blank-space": "^0.9.0",
43
43
  "xstate": "^5.18.0",
44
44
  "zod": "^4.4.3",
45
- "@jr2/agent-protocol": "0.5.1"
45
+ "@jr2/agent-protocol": "0.6.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^26.0.1",
49
- "@jr2/harness": "0.5.1"
49
+ "@jr2/harness": "0.6.0"
50
50
  },
51
51
  "files": [
52
52
  "bin",