@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
@@ -5,9 +5,9 @@ description: "Every route of the Orchestrator's HTTP API, by access band, and th
5
5
 
6
6
  # HTTP API
7
7
 
8
- The Orchestrator's HTTP API: what the `jr2` CLI, the Console, a webhook and the Harness call. Each route is in one access band, by the credential it asks for ([ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)). Send a token as `Authorization: Bearer <token>`. An error answer is `{ error }`, with the status the route gives. A path segment `:name` is a parameter; `:name{regex}` is a parameter that must match the regex.
8
+ The Orchestrator's HTTP API: what the `jr2` CLI, the Console, a webhook and the Harness call. Each route is in one access band, by the credential it asks for ([ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). Send a token as `Authorization: Bearer <token>`. An error answer is `{ error }`, with the status the route gives. A path segment `:name` is a parameter; `:name{regex}` is a parameter that must match the regex.
9
9
 
10
- Bands: [Open](#band-open), [Authenticated](#band-authenticated), [Instance token only](#band-instanceonly), [Publish token only](#band-publishonly). The types the bodies and answers name are in [Types](#types).
10
+ Bands: [Open](#band-open), [Authenticated](#band-authenticated), [A Caller with a scope](#band-guarded), [Instance token only](#band-instanceonly), [Publish token only](#band-publishonly), [A Hook's verifier](#band-hook). The types the bodies and answers name are in [Types](#types).
11
11
 
12
12
  <a id="band-open"></a>
13
13
 
@@ -46,10 +46,10 @@ No token. Structure and observation: no run context, and no control.
46
46
 
47
47
  ### `GET /assembly`
48
48
 
49
- - Summary: The current Assembly and the Assemblies that live runs hold.
50
- - Response: [`AssemblyView`](#type-assemblyview)
49
+ - Summary: The current Assembly, the Assemblies that live runs hold, and each standing Workflow: live, paused or waiting, its run, and how many items it holds.
50
+ - Response: [`AssemblyView`](#type-assemblyview) & { standing?: [`StandingView`](#type-standingview)[] }
51
51
  - Stream: no
52
- - Decisions: [ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)
52
+ - Decisions: [ADR-0017](decisions/0017-pool-over-a-source-port.md), [ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)
53
53
 
54
54
  <a id="get-workflows-wrapper-chart-schema"></a>
55
55
 
@@ -76,7 +76,16 @@ No token. Structure and observation: no run context, and no control.
76
76
  - Summary: The Console page to a browser; a 404 to a JSON caller.
77
77
  - Response: text/html (the Console shell)
78
78
  - Stream: no
79
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md)
79
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md)
80
+
81
+ <a id="get-console-config"></a>
82
+
83
+ ### `GET /console/config`
84
+
85
+ - Summary: What the Console needs before a login: each JWT issuer that offers a login, and its client id. Both are public values.
86
+ - Response: { login: Array<{ issuer, clientId }> }
87
+ - Stream: no
88
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
80
89
 
81
90
  <a id="get-workflows-name"></a>
82
91
 
@@ -85,7 +94,7 @@ No token. Structure and observation: no run context, and no control.
85
94
  - Summary: The Console page to a browser; to a JSON caller, the workflow and its declared input schema.
86
95
  - Response: { name, machineId, input: JSON Schema | null }, or text/html
87
96
  - Stream: no
88
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md), [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md)
97
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md), [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md)
89
98
 
90
99
  <a id="get-workflows-name-machine"></a>
91
100
 
@@ -130,7 +139,7 @@ No token. Structure and observation: no run context, and no control.
130
139
  - Summary: The Console's stylesheet.
131
140
  - Response: text/css
132
141
  - Stream: no
133
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md)
142
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md)
134
143
 
135
144
  <a id="get-assets-elk-js"></a>
136
145
 
@@ -139,7 +148,7 @@ No token. Structure and observation: no run context, and no control.
139
148
  - Summary: The Console's graph layout library (the elkjs bundle).
140
149
  - Response: text/javascript
141
150
  - Stream: no
142
- - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-the-instance-token.md)
151
+ - Decisions: [ADR-0032](decisions/0032-the-console-unlocks-with-a-callers-credential.md)
143
152
 
144
153
  <a id="get-assets-file"></a>
145
154
 
@@ -178,8 +187,8 @@ Any token jr2 minted: the Instance token, or a Sandbox token. A Sandbox token re
178
187
 
179
188
  ### `GET /agents/:instanceId/surface`
180
189
 
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 }> }
190
+ - Summary: An Agent's Menu, this Turn's Allowed picks, the conversation's pending notices and its slot's Steering messages. A control token reads only its own pod's, for the slot it states.
191
+ - Response: [`AgentSurfaceView`](#type-agentsurfaceview) & { notices: Array<{ id, notice }>, steering?: Array<{ id, message }> }
183
192
  - Stream: no
184
193
  - 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
194
 
@@ -206,12 +215,22 @@ Any token jr2 minted: the Instance token, or a Sandbox token. A Sandbox token re
206
215
 
207
216
  ### `POST /agents/:instanceId/notices`
208
217
 
209
- - Summary: Acknowledge the notices a step boundary appended to the Turn: they are heard in its conversation.
218
+ - Summary: Acknowledge the notices and Steering messages a step boundary appended to the Turn: they are heard in its conversation.
210
219
  - Body: { heard: string[] }
211
220
  - Response: { heard: number }
212
221
  - Stream: no
213
222
  - 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
223
 
224
+ <a id="post-agents-instanceid-conversation"></a>
225
+
226
+ ### `POST /agents/:instanceId/conversation`
227
+
228
+ - Summary: Keep a batch of a conversation's entries. Only the control token of the pod that the run's admission ledger names, for the ledger's slot; never the Instance token. An entry kept already adds nothing.
229
+ - Body: { entries: Array<{ id, seq, at, kind, text }> }, at most 4 MiB
230
+ - Response: { kept, cut }; 409 while the run's ledger does not name the conversation yet (send again); 404 for a run that is not known
231
+ - Stream: no
232
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0076](decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)
233
+
215
234
  <a id="post-sandboxes-name-fetch"></a>
216
235
 
217
236
  ### `POST /sandboxes/:name/fetch`
@@ -222,115 +241,209 @@ Any token jr2 minted: the Instance token, or a Sandbox token. A Sandbox token re
222
241
  - Stream: no
223
242
  - Decisions: [ADR-0053](decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md)
224
243
 
225
- <a id="band-instanceonly"></a>
244
+ <a id="band-guarded"></a>
226
245
 
227
- ## Instance token only
228
-
229
- The Instance token alone: run control, starting a run, and full run state. A Sandbox token is refused.
246
+ ## A Caller with a scope
230
247
 
231
- <a id="put-workflows-wrapper-charts-name"></a>
232
-
233
- ### `PUT /workflows/:wrapper/charts/:name`
234
-
235
- - Summary: Admit and keep a runtime Chart under a name. Its Workflow is `<wrapper>/<name>`.
236
- - Body: the Chart text alone
237
- - Response: 201 { name, origin, hash } when new, 200 when it replaced one; 422 { error, report } when refused
238
- - Stream: no
239
- - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
248
+ A Caller: the Instance token, a named Caller token, or a JWT. Each route needs one scope, for the Workflow of the run or the door that it addresses, and a 403 names the scope and the Workflow. The Instance token has every scope. A Sandbox token is not a Caller, and is refused. A Caller can state the party it acts for in the header `jr2-on-behalf-of`; jr2 records the claim and does not check it.
240
249
 
241
- <a id="delete-workflows-wrapper-charts-name"></a>
250
+ <a id="get-whoami"></a>
242
251
 
243
- ### `DELETE /workflows/:wrapper/charts/:name`
252
+ ### `GET /whoami`
244
253
 
245
- - Summary: Remove a runtime Chart. A live run of it keeps its own text.
246
- - Response: 204
254
+ - Summary: The request's Caller: how jr2 knows it, its name, what it may do, and the party it says it acts for.
255
+ - Scope: any Caller
256
+ - Response: [`Caller`](#type-caller)
247
257
  - Stream: no
248
- - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
258
+ - Decisions: [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
249
259
 
250
260
  <a id="post-workflows-wrapper-charts-name-runs"></a>
251
261
 
252
262
  ### `POST /workflows/:wrapper/charts/:name/runs`
253
263
 
254
264
  - Summary: Start a run of a named Chart, as `POST /workflows/<wrapper>%2F<name>/runs` does.
265
+ - Scope: `start`
255
266
  - Body: the Chart wrapper's input, without the Chart
256
267
  - Response: 201 { runId, instanceId }; 409 when the named Chart cannot run now
257
268
  - Stream: no
258
- - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
269
+ - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
259
270
 
260
271
  <a id="post-workflows-name-runs"></a>
261
272
 
262
273
  ### `POST /workflows/:name/runs`
263
274
 
264
- - Summary: Start a run of a registered workflow. A `dispatch` from another Orchestrator sends the header `jr2-dispatched-by`, and a repeated start with its key answers the run that it started.
275
+ - Summary: Start a run of a registered workflow. A `dispatch` from another Orchestrator sends the header `jr2-dispatched-by`, and a repeated start with its key answers the run that it started. With the header `jr2-key-only: true` too, the start is a key-only ask: it answers the run under the key, and starts nothing. For a standing Workflow, jr2 starts the run: the start only resumes a paused one, and `?drop-held=true` resumes it with no held item.
276
+ - Scope: `start`
265
277
  - Body: the workflow's input, checked against its declared input schema
266
- - Response: 201 { runId, instanceId }; 200 { runId, instanceId, existing: true } for a repeated dispatch; 422 { error, report } when the door refuses a Chart in the input
278
+ - Response: 201 { runId, instanceId }; 201 { runId, instanceId, resumed: true } for a resumed standing Workflow; 200 { runId, instanceId, existing: true } for a repeated dispatch; 200 { existing: false } for a key-only ask with no run under the key; 409 for a standing Workflow that is not paused, or a dispatch to one; 422 { error, report } when the door refuses a Chart in the input
267
279
  - Stream: no
268
- - Decisions: [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md), [ADR-0058](decisions/0058-a-harness-answers-the-orchestrator-alone.md), [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md), [ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)
280
+ - Decisions: [ADR-0017](decisions/0017-pool-over-a-source-port.md), [ADR-0033](decisions/0033-a-machine-declares-the-input-that-starts-it.md), [ADR-0058](decisions/0058-a-harness-answers-the-orchestrator-alone.md), [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md), [ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
269
281
 
270
282
  <a id="get-runs"></a>
271
283
 
272
284
  ### `GET /runs`
273
285
 
274
- - Summary: The live runs, with their full status.
275
- - Response: [`RunStatus`](#type-runstatus)[]
286
+ - Summary: A page of the runs of the Workflows that the Caller may read, newest first: live runs by default; ?status=live|ended|all|<list> ?workflow ?since ?cursor ?limit.
287
+ - Scope: `read`
288
+ - Response: { runs: [`RunSummary`](#type-runsummary)[], next? }
276
289
  - Stream: no
277
- - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)
290
+ - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
278
291
 
279
- <a id="get-repos"></a>
292
+ <a id="get-runs-resolve"></a>
280
293
 
281
- ### `GET /repos`
294
+ ### `GET /runs/resolve`
282
295
 
283
- - Summary: The Repos as the cluster reports them.
284
- - Response: { dataPlane, repos: [`RepoStatus`](#type-repostatus)[] }
296
+ - Summary: The run ids that start with a prefix (`?prefix=`, at least 4 characters), of the Workflows in any scope of the Caller.
297
+ - Scope: any Caller
298
+ - Response: { prefix, runIds: string[], truncated }
285
299
  - Stream: no
286
- - Decisions: [ADR-0048](decisions/0048-the-orchestrator-boots-without-its-repos.md), [ADR-0051](decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md)
300
+ - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
287
301
 
288
- <a id="get-runs-resolve"></a>
302
+ <a id="get-gates"></a>
289
303
 
290
- ### `GET /runs/resolve`
304
+ ### `GET /gates`
291
305
 
292
- - Summary: The run ids that start with a prefix (`?prefix=`, at least 4 characters).
293
- - Response: { prefix, runIds: string[], truncated }
306
+ - Summary: The Gate inbox: every open Gate of every live run that the Caller may answer, oldest first, never a run's context; ?workflow.
307
+ - Scope: `answer`
308
+ - Response: { gates: [`GateInboxEntry`](#type-gateinboxentry)[] }
294
309
  - Stream: no
295
- - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md)
310
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
296
311
 
297
312
  <a id="get-runs-runid"></a>
298
313
 
299
314
  ### `GET /runs/:runId`
300
315
 
301
- - Summary: One run's status, live or settled, with its open Gates.
302
- - Response: [`RunStatus`](#type-runstatus) & { gates: [`GateView`](#type-gateview)[] }
316
+ - Summary: One run's status, live or settled, with its open Gates. `read` opens the status, and `answer` opens the Gates that the Caller may answer: with `answer` alone the answer is the run's observation and those Gates, and never its context.
317
+ - Scope: `read` or `answer`; each opens its own part of the answer
318
+ - Response: [`RunStatus`](#type-runstatus) & { gates: [`GateView`](#type-gateview)[] }; [`RunStatus`](#type-runstatus) with `read` alone; [`RunObservation`](#type-runobservation) & { gates: [`GateView`](#type-gateview)[] } with `answer` alone
303
319
  - Stream: no
304
- - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0011](decisions/0011-workflow-defined-events.md)
320
+ - Decisions: [ADR-0009](decisions/0009-cli-and-instance-interface.md), [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
305
321
 
306
322
  <a id="post-runs-runid-gates-gate-events"></a>
307
323
 
308
324
  ### `POST /runs/:runId/gates/:gate/events`
309
325
 
310
- - Summary: Deliver an event to an open Gate of a run.
326
+ - Summary: Deliver an event to an open Gate of a run. A Caller whose `answer` names single Gates of the Workflow is refused 403 for another Gate.
327
+ - Scope: `answer`
311
328
  - Body: { type, ...payload }, checked against the Gate's schema for that event
312
329
  - Response: { ok: true }; 409 { error, acceptsNow } when the run does not take it now
313
330
  - Stream: no
314
- - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md)
331
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0013](decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
315
332
 
316
333
  <a id="get-runs-runid-events"></a>
317
334
 
318
335
  ### `GET /runs/:runId/events`
319
336
 
320
337
  - Summary: One run's full activity until it settles; a settled run's final status, once.
321
- - Response: SSE: status, emit, retry, admission, pick, placing, placed, child-error, effect, gone
338
+ - Scope: `read`
339
+ - Response: SSE: status, emit, retry, admission, pick, placing, placed, child-error, looked, hook-refused, pool, effect, gone
322
340
  - Stream: yes: Server-Sent Events
323
- - Decisions: [ADR-0022](decisions/0022-observation-is-a-level-triggered-feed.md), [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0064](decisions/0064-a-workspace-waits-for-capacity.md), [ADR-0067](decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)
341
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0022](decisions/0022-observation-is-a-level-triggered-feed.md), [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0064](decisions/0064-a-workspace-waits-for-capacity.md), [ADR-0067](decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
342
+
343
+ <a id="get-runs-runid-conversations"></a>
344
+
345
+ ### `GET /runs/:runId/conversations`
346
+
347
+ - Summary: A run's kept conversations, the first first: one row for each conversation of each Agent, Menu-only Agents too, with its Agent slot.
348
+ - Scope: `read`
349
+ - Response: Array<{ agent, slot?, instanceId, open, bytes, cut, swept?: "no space" }>
350
+ - Stream: no
351
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md), [ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)
352
+
353
+ <a id="get-runs-runid-conversations-instanceid"></a>
354
+
355
+ ### `GET /runs/:runId/conversations/:instanceId`
356
+
357
+ - Summary: The entries of one kept conversation after a sequence number (`?after=<seq>`, `?limit=`, 1000 by default). The Instance ID is URL-encoded.
358
+ - Scope: `read`
359
+ - Response: Array<{ id, seq, at, agent, kind, text }>
360
+ - Stream: no
361
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md)
362
+
363
+ <a id="get-runs-runid-conversations-instanceid-events"></a>
364
+
365
+ ### `GET /runs/:runId/conversations/:instanceId/events`
366
+
367
+ - Summary: Follow one kept conversation: its entries after `?after=<seq>`, then each new one, until the conversation is not open.
368
+ - Scope: `read`
369
+ - Response: SSE: entry, end
370
+ - Stream: yes: Server-Sent Events
371
+ - Decisions: [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md)
324
372
 
325
373
  <a id="post-runs-runid-events"></a>
326
374
 
327
375
  ### `POST /runs/:runId/events`
328
376
 
329
377
  - Summary: Cancel a run: it ends, and its Workspaces go unless `keep` is true.
378
+ - Scope: `cancel`
330
379
  - Body: { type: "CANCEL", keep?: boolean }
331
380
  - Response: { ok: true }
332
381
  - Stream: no
333
- - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0025](decisions/0025-cancel-ends-a-run-stop-parks-it.md)
382
+ - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0025](decisions/0025-cancel-ends-a-run-stop-parks-it.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
383
+
384
+ <a id="get-runs-runid-agents"></a>
385
+
386
+ ### `GET /runs/:runId/agents`
387
+
388
+ - Summary: The Agent slots of a run that a Steering message can go to: one row for each Agent of each live Machine instance.
389
+ - Scope: `steer`
390
+ - Response: Array<{ agent, open, steerable }>; [] for a run that is not live
391
+ - Stream: no
392
+ - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
393
+
394
+ <a id="post-runs-runid-steering"></a>
395
+
396
+ ### `POST /runs/:runId/steering`
397
+
398
+ - Summary: Send a Steering message to one Agent slot of a live run: its open Turn hears it at a step boundary, or else its next Turn.
399
+ - Scope: `steer`
400
+ - Body: { agent, text }, `text` at most 16 KiB
401
+ - Response: 202 { id, agent, open }; 413 for a longer text; 409 { error, agents? } when no live Machine instance holds the slot; 403 when the Agent is not steerable
402
+ - Stream: no
403
+ - Decisions: [ADR-0002](decisions/0002-duplex-streaming-actor.md), [ADR-0062](decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
404
+
405
+ <a id="band-instanceonly"></a>
406
+
407
+ ## Instance token only
408
+
409
+ The Instance token alone: the routes that no scope opens. A named Caller token is refused, whatever its scopes, and so is a Sandbox token.
410
+
411
+ <a id="get-hooks-name"></a>
412
+
413
+ ### `GET /hooks/:name`
414
+
415
+ - Summary: A Hook's log, newest first: each delivery that jr2 keeps (`runs.retain`, 30 days by default), with what the door did with it. `?limit=` (default 50, at most 1000).
416
+ - Response: { hook, declared, lines: [`HookLogLine`](#type-hooklogline)[] }
417
+ - Stream: no
418
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)
419
+
420
+ <a id="put-workflows-wrapper-charts-name"></a>
421
+
422
+ ### `PUT /workflows/:wrapper/charts/:name`
423
+
424
+ - Summary: Admit and keep a runtime Chart under a name. Its Workflow is `<wrapper>/<name>`.
425
+ - Body: the Chart text alone
426
+ - Response: 201 { name, origin, hash } when new, 200 when it replaced one; 422 { error, report } when refused
427
+ - Stream: no
428
+ - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
429
+
430
+ <a id="delete-workflows-wrapper-charts-name"></a>
431
+
432
+ ### `DELETE /workflows/:wrapper/charts/:name`
433
+
434
+ - Summary: Remove a runtime Chart. A live run of it keeps its own text.
435
+ - Response: 204
436
+ - Stream: no
437
+ - Decisions: [ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)
438
+
439
+ <a id="get-repos"></a>
440
+
441
+ ### `GET /repos`
442
+
443
+ - Summary: The Repos as the cluster reports them.
444
+ - Response: { dataPlane, repos: [`RepoStatus`](#type-repostatus)[] }
445
+ - Stream: no
446
+ - Decisions: [ADR-0048](decisions/0048-the-orchestrator-boots-without-its-repos.md), [ADR-0051](decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md)
334
447
 
335
448
  <a id="band-publishonly"></a>
336
449
 
@@ -344,12 +457,154 @@ The publish token alone, which `jr2 up` reads from the instance Secret. The Inst
344
457
 
345
458
  - Summary: Publish the next Assembly. `jr2 up` calls it, and nothing else. A refused Assembly leaves the current one current.
346
459
  - Body: the Assembly as a tar stream, with the headers `jr2-kit-version` and `jr2-assembly-hash`
347
- - Response: 200 with the Assembly, its Workflows, the held Assemblies and the ones collected; 400, 409 or 422 with the reason
460
+ - Response: 200 with the Assembly, its Workflows, the held Assemblies, the ones collected and each standing Workflow's state; 400, 409 or 422 with the reason
348
461
  - Stream: no
349
462
  - Decisions: [ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)
350
463
 
464
+ <a id="band-hook"></a>
465
+
466
+ ## A Hook's verifier
467
+
468
+ No bearer token: the Hook's own verifier checks each delivery, as its kind tells (a `token` header, an `ed25519` signature or a `mac`). A delivery that fails the verifier, or whose signed time is more than 300 seconds from the Orchestrator's clock, gets 401.
469
+
470
+ <a id="post-hooks-name"></a>
471
+
472
+ ### `POST /hooks/:name`
473
+
474
+ - Summary: A delivery of an outside service to the Hook that the current Assembly's config names. The Hook's verifier authenticates it, and it goes to the open Gates whose handlers hear its kind and event key. 413 for a body above 2 MiB, before the verifier and with no more of the body read than that; 401 for a delivery that fails the verifier, 404 for a name that no Hook has, 503 with no current Assembly, and 503 when a standing Pool that hears it has no live run and is not paused, or when the delivery followed the Pool's hand-overs 3 times and the Pool handed its run over again. A 503 claims no delivery id.
475
+ - Body: the service's own request
476
+ - Response: the kind's answer to a handshake, or [`HookOutcome`](#type-hookoutcome): { delivery, seen, delivered, refused, ignored, sources, payloadRefused? }
477
+ - Stream: no
478
+ - Decisions: [ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)
479
+
351
480
  ## Types
352
481
 
482
+ <a id="type-caller"></a>
483
+
484
+ ### `Caller`
485
+
486
+ ```ts
487
+ type Caller = {
488
+ via: "token" | "jwt" | "hook";
489
+ name: string;
490
+ shown?: string;
491
+ may: Scopes;
492
+ onBehalfOf?: string;
493
+ };
494
+ ```
495
+
496
+ A request's Caller ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): who acts, through which authenticator, and what it may do.
497
+
498
+ **Fields**
499
+
500
+ - `via`: `"token" | "jwt" | "hook"`
501
+
502
+ The authenticator that made the Caller: `token` (the Instance token or a named token), `jwt`
503
+ or `hook`.
504
+ - `name`: `string`
505
+
506
+ The Caller's name for that authenticator: `instance`, a named token's name, a JWT's `sub`, a
507
+ Hook's name.
508
+ - `shown` (optional): `string`
509
+
510
+ The name that jr2 shows, when it is not `name` (a JWT's `email`).
511
+ - `may`: [`Scopes`](config.md#callers-tokens-name-may)
512
+
513
+ What the Caller may do.
514
+ - `onBehalfOf` (optional): `string`
515
+
516
+ The Caller's claim about the party it acts for. jr2 records it and never checks it.
517
+
518
+ <a id="type-hookoutcome"></a>
519
+
520
+ ### `HookOutcome`
521
+
522
+ ```ts
523
+ type HookOutcome = {
524
+ delivery: string;
525
+ seen: boolean;
526
+ delivered: HookFanIn["delivered"];
527
+ refused: HookFanIn["refused"];
528
+ ignored: number;
529
+ sources: HookFanIn["sources"];
530
+ payloadRefused?: { kind: string; key: string; issues: unknown[] };
531
+ };
532
+ ```
533
+
534
+ The answer to one delivery ([ADR-0011](decisions/0011-workflow-defined-events.md)): `delivery` is its id, `seen` says it was a repeat,
535
+ `delivered` the Gates that took it, `refused` the Gates that did not and why, `ignored` how many
536
+ open Gates of its kind and key gave nothing, and `sources` each Source's verdict ([ADR-0017](decisions/0017-pool-over-a-source-port.md)).
537
+ `payloadRefused` names the kind and the event key of a payload that the parse refused, with zod's
538
+ issues.
539
+
540
+ **Fields**
541
+
542
+ - `delivery`: `string`
543
+ - `seen`: `boolean`
544
+ - `delivered`: `HookFanIn["delivered"]`
545
+ - `refused`: `HookFanIn["refused"]`
546
+ - `ignored`: `number`
547
+ - `sources`: `HookFanIn["sources"]`
548
+ - `payloadRefused` (optional): `object`
549
+
550
+ - `kind`: `string`
551
+ - `key`: `string`
552
+ - `issues`: `unknown[]`
553
+
554
+ <a id="type-hooklogline"></a>
555
+
556
+ ### `HookLogLine`
557
+
558
+ ```ts
559
+ type HookLogLine = {
560
+ hook: string;
561
+ at: string;
562
+ kind:
563
+ | "pending"
564
+ | "delivered"
565
+ | "seen"
566
+ | "hold-full"
567
+ | "no-live-run"
568
+ | "handshake"
569
+ | "payload-refused"
570
+ | "unreadable"
571
+ | "too-large"
572
+ | "follow-up";
573
+ status: number;
574
+ delivery?: string;
575
+ digest?: string;
576
+ key?: string;
577
+ detail?: unknown;
578
+ };
579
+ ```
580
+
581
+ One line of the hook log ([ADR-0011](decisions/0011-workflow-defined-events.md)): what the door did with one delivery of one Hook, and when.
582
+ `kind` says which line it is: `delivered` (the fan-in ran; `outcome` holds what it did), `seen` (a
583
+ repeat), `hold-full` (the fan-in ran and every Source was full, [ADR-0017](decisions/0017-pool-over-a-source-port.md): the line does not claim
584
+ the id, so a send again is heard), `no-live-run` (the fan-in ran, and a standing Pool that hears the
585
+ kind and key has no live run and is not paused: 503, and the line does not claim the id either),
586
+ `handshake`, `payload-refused` (with zod's `issues`), `unreadable` (the kind could not
587
+ read the request: `error`) or `follow-up` (the follow-up post of an answer that a person reads
588
+ failed, or went to a url outside the kind's `posts`: `post` and `error`; its `status` is 0). A
589
+ `reply` that threw is in the `replyErrors` of its delivery's `detail`. `status` is the status that
590
+ the rules give, which is the status of the answer unless a person reads the answer: that answer is
591
+ 200 ([ADR-0011](decisions/0011-workflow-defined-events.md)).
592
+
593
+ **Fields**
594
+
595
+ - `hook`: `string`
596
+ - `at`: `string`
597
+ - `kind`: `"pending" | "delivered" | "seen" | "hold-full" | "no-live-run" | "handshake" | "payload-refused" | "unreadable" | "too-large" | "follow-up"`
598
+ - `status`: `number`
599
+ - `delivery` (optional): `string`
600
+ - `digest` (optional): `string`
601
+ - `key` (optional): `string`
602
+
603
+ The event key, when the kind read one.
604
+ - `detail` (optional): `unknown`
605
+
606
+ What the line says: the outcome, zod's issues, the error.
607
+
353
608
  <a id="type-workflowlisting"></a>
354
609
 
355
610
  ### `WorkflowListing`
@@ -401,6 +656,40 @@ type AssemblyView = {
401
656
  - `current`: `{ hash: string; kitVersion: string; workflows: string[]; } | null`
402
657
  - `held`: `HeldAssembly[]`
403
658
 
659
+ <a id="type-standingview"></a>
660
+
661
+ ### `StandingView`
662
+
663
+ ```ts
664
+ type StandingView = {
665
+ workflow: string;
666
+ pool: string;
667
+ callee: string;
668
+ state: "live" | "paused" | "waiting";
669
+ run?: string;
670
+ held: number;
671
+ why?: "cancel" | "errors" | "bound";
672
+ };
673
+ ```
674
+
675
+ A standing Workflow as `jr2 up` and `jr2 status` print it ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): its Pool, its callee, its
676
+ state, its run, and how many items it holds. `live`: a run is live. `paused`: a cancel (`why`:
677
+ `cancel`), or a fourth run in a row in which no item that the run took started a run, after 3
678
+ hand-overs: the last one ended `error` or `failed` (`errors`), or took its bound (`bound`). The
679
+ held items are in the blob of `run`.
680
+ `waiting`: no run is live, and jr2 starts one after the back-off. Structure and counts only: no
681
+ key and no item, so it is on the open band with the Assembly.
682
+
683
+ **Fields**
684
+
685
+ - `workflow`: `string`
686
+ - `pool`: `string`
687
+ - `callee`: `string`
688
+ - `state`: `"live" | "paused" | "waiting"`
689
+ - `run` (optional): `string`
690
+ - `held`: `number`
691
+ - `why` (optional): `"cancel" | "errors" | "bound"`
692
+
404
693
  <a id="type-machinedoc"></a>
405
694
 
406
695
  ### `MachineDoc`
@@ -443,7 +732,7 @@ type RunObservation = {
443
732
  children: RunChild[];
444
733
  bound?: true;
445
734
  chartHashes?: string[];
446
- dispatchedBy?: Omit<DispatchedBy, "effect">;
735
+ dispatchedBy?: Omit<DispatchedBy, "effect" | "key" | "hook">;
447
736
  dispatched?: Array<{ slot: string; run: string }>;
448
737
  };
449
738
  ```
@@ -479,7 +768,7 @@ parallel work (`"F-1"`) — the same class of thing as a state key.
479
768
 
480
769
  The hashes of the Charts it binds now (see `RunStatus.chartHashes`). Structure: a hash
481
770
  of a Chart's text, which `runMachine` already serves on the open band.
482
- - `dispatchedBy` (optional): `Omit<DispatchedBy, "effect">`
771
+ - `dispatchedBy` (optional): `Omit<DispatchedBy, "effect" | "key" | "hook">`
483
772
 
484
773
  The run that dispatched this one, its slot, and the chain's depth ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)). Routing — two
485
774
  ids, a slot name and a number — the class of thing `runId` and `value` are, so [ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)'s line
@@ -489,13 +778,116 @@ parallel work (`"F-1"`) — the same class of thing as a state key.
489
778
  The runs it dispatched, by slot ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)): the same routing as `dispatchedBy`, the other
490
779
  direction.
491
780
 
781
+ <a id="type-runsummary"></a>
782
+
783
+ ### `RunSummary`
784
+
785
+ ```ts
786
+ type RunSummary = {
787
+ runId: string;
788
+ workflow: string;
789
+ status: ShownStatus;
790
+ reason?: string;
791
+ startedBy?: RunStarter;
792
+ startedAt: string;
793
+ endedAt?: string;
794
+ assembly?: string;
795
+ openGates: number;
796
+ placing?: string[];
797
+ dispatchedBy?: DispatchedBy;
798
+ swept?: "conversation swept: no space";
799
+ };
800
+ ```
801
+
802
+ One run as `GET /runs` lists it ([ADR-0009](decisions/0009-cli-and-instance-interface.md)): a summary, not the whole `RunStatus`. `status` is
803
+ `active` for a live run, else the store's word for how it ended. `reason` says why, where the
804
+ Machine did not: the host's reason (`drifted`, `lost`, [ADR-0030](decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md)), the Failure's reason
805
+ (`failed`, [ADR-0073](decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)) or the fault (`error`). `endedAt` is the terminal write of an ended run.
806
+ `openGates` counts the Gates open now, so it is 0 for an ended run. `placing` names the child
807
+ paths of the Workspaces that wait for capacity ([ADR-0064](decisions/0064-a-workspace-waits-for-capacity.md)), and `dispatchedBy` the run that
808
+ started this one ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)).
809
+
810
+ **Fields**
811
+
812
+ - `runId`: `string`
813
+ - `workflow`: `string`
814
+ - `status`: [`ShownStatus`](#type-shownstatus)
815
+ - `reason` (optional): `string`
816
+ - `startedBy` (optional): [`RunStarter`](#type-runstarter)
817
+ - `startedAt`: `string`
818
+ - `endedAt` (optional): `string`
819
+ - `assembly` (optional): `string`
820
+ - `openGates`: `number`
821
+ - `placing` (optional): `string[]`
822
+ - `dispatchedBy` (optional): [`DispatchedBy`](#type-dispatchedby)
823
+ - `swept` (optional): `"conversation swept: no space"`
824
+
825
+ The space guard deleted the run's kept conversations ([ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)); the row stays.
826
+
827
+ <a id="type-repostatus"></a>
828
+
829
+ ### `RepoStatus`
830
+
831
+ ```ts
832
+ type RepoStatus = {
833
+ key: string;
834
+ url: string;
835
+ identity?: string;
836
+ bound: boolean;
837
+ lastAttached?: string;
838
+ nodes: RepoNodeState[];
839
+ };
840
+ ```
841
+
842
+ One `Repo` resource as the instance reports it (`GET /repos`).
843
+
844
+ **Fields**
845
+
846
+ - `key`: `string`
847
+
848
+ The cache key: the resource's name, the hostPath leaf, `/repos/<key>`.
849
+ - `url`: `string`
850
+
851
+ The Binding's own spelling — what the cache clones.
852
+ - `identity` (optional): `string`
853
+ - `bound`: `boolean`
854
+
855
+ A registered Machine binds it — never evicted by `jr2 gc`.
856
+ - `lastAttached` (optional): `string`
857
+
858
+ When a run last attached it — the eviction clock for a Repo nothing binds. Absent while only
859
+ the boot has stated it: a bound Repo no run has attached yet.
860
+ - `nodes`: `RepoNodeState[]`
861
+
862
+ <a id="type-gateinboxentry"></a>
863
+
864
+ ### `GateInboxEntry`
865
+
866
+ ```ts
867
+ type GateInboxEntry = { runId: string; workflow: string } & GateView & {
868
+ openedAt: string;
869
+ hook?: string[];
870
+ };
871
+ ```
872
+
873
+ One entry of the Gate inbox (`GET /gates` — [ADR-0011](decisions/0011-workflow-defined-events.md)): an open Gate as [`GateView`](#type-gateview) gives it,
874
+ with its run, the run's Workflow, and when it opened, ISO-8601. `hook` names the Hook kinds that
875
+ the Gate hears, when it hears one. Never the run's context ([ADR-0014](decisions/0014-observation-is-open-run-state-is-not.md)).
876
+
877
+ **Fields**: each field of [`GateView`](#type-gateview), and these:
878
+
879
+ - `runId`: `string`
880
+ - `workflow`: `string`
881
+ - `openedAt`: `string`
882
+ - `hook` (optional): `string[]`
883
+
492
884
  <a id="type-runstatus"></a>
493
885
 
494
886
  ### `RunStatus`
495
887
 
496
888
  ```ts
497
889
  type RunStatus = RunRecord & {
498
- status: string;
890
+ status: ShownStatus;
499
891
  value: unknown;
500
892
  context: unknown;
501
893
  children: RunChild[];
@@ -504,10 +896,15 @@ type RunStatus = RunRecord & {
504
896
  fault?: string;
505
897
  reason?: string;
506
898
  assembly?: string;
899
+ swept?: "conversation swept: no space";
507
900
  bound?: true;
508
901
  chartHashes?: string[];
509
902
  output?: unknown;
510
903
  dispatched?: Dispatched[];
904
+ cancelledBy?: CallerRecord;
905
+ handedOverTo?: string;
906
+ answers?: GateAnswer[];
907
+ steering?: SteeringRecord[];
511
908
  worktrees?: RunWorktree[];
512
909
  };
513
910
  ```
@@ -518,7 +915,7 @@ A run's current observable state. `fault` carries the error message when status
518
915
 
519
916
  **Fields**: each field of [`RunRecord`](#type-runrecord), and these:
520
917
 
521
- - `status`: `string`
918
+ - `status`: [`ShownStatus`](#type-shownstatus)
522
919
  - `value`: `unknown`
523
920
  - `context`: `unknown`
524
921
  - `children`: `RunChild[]`
@@ -532,7 +929,7 @@ A run's current observable state. `fault` carries the error message when status
532
929
  - `held`: `RunHeld[]`
533
930
 
534
931
  Every pick of this run that is outstanding ([ADR-0024](decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)): the Agent, its pick, and since when —
535
- the Console's "held since". There is no default time bound on a held Turn; this is how a
932
+ the Console's "outstanding since". There is no default time bound on a held Turn; this is how a
536
933
  human sees one. Behind the token, like `context`: a pick names what the Agent did.
537
934
  - `fault` (optional): `string`
538
935
  - `reason` (optional): `string`
@@ -545,6 +942,10 @@ A run's current observable state. `fault` carries the error message when status
545
942
 
546
943
  The hash of the Assembly the run started under ([ADR-0069](decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)). Absent for a run no Assembly
547
944
  started.
945
+ - `swept` (optional): `"conversation swept: no space"`
946
+
947
+ The space guard deleted the run's kept conversations ([ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md), [ADR-0023](decisions/0023-the-harness-prints-the-conversation.md)): `jr2 logs` and
948
+ the Console show this in place of them. The run's row stays.
548
949
  - `bound` (optional): `true`
549
950
 
550
951
  The run binds at least one Chart ([ADR-0070](decisions/0070-a-machine-lends-its-parts-to-a-chart.md)): its Machine is the bound one, `runMachine`.
@@ -565,6 +966,23 @@ A run's current observable state. `fault` carries the error message when status
565
966
  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
566
967
  direction, read off the caller's own ledger rows. A foreign dispatch's run is a run of the
567
968
  Orchestrator its target names. Absent when it started none.
969
+ - `cancelledBy` (optional): [`CallerRecord`](authoring-surface/type-callerrecord.md)
970
+
971
+ The Caller that cancelled the run ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). Behind the token: `observe` never
972
+ carries it.
973
+ - `handedOverTo` (optional): `string`
974
+
975
+ The run that took over from this standing run ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): a hand-over ends the run `done`.
976
+ - `answers` (optional): `GateAnswer[]`
977
+
978
+ The answers to the run's Gates, oldest first, each with the Caller that gave it ([ADR-0011](decisions/0011-workflow-defined-events.md),
979
+ [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): the last `GATE_ANSWERS_KEPT`. An event that a Gate's `look` gives is not an
980
+ answer, and is not here. Behind the token. Absent when no Gate was answered.
981
+ - `steering` (optional): `SteeringRecord[]`
982
+
983
+ The run's Steering messages, oldest first ([ADR-0002](decisions/0002-duplex-streaming-actor.md)): each one's Agent slot, author, text,
984
+ time and outcome — the newest 100, and each one still held. Behind the token, like `context`.
985
+ Absent when the run got none.
568
986
  - `worktrees` (optional): `RunWorktree[]`
569
987
 
570
988
  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
@@ -572,41 +990,6 @@ A run's current observable state. `fault` carries the error message when status
572
990
  is one entry. Behind the token, like `context`: a branch name is the workflow's data. Absent
573
991
  when the run cut none.
574
992
 
575
- <a id="type-repostatus"></a>
576
-
577
- ### `RepoStatus`
578
-
579
- ```ts
580
- type RepoStatus = {
581
- key: string;
582
- url: string;
583
- identity?: string;
584
- bound: boolean;
585
- lastAttached?: string;
586
- nodes: RepoNodeState[];
587
- };
588
- ```
589
-
590
- One `Repo` resource as the instance reports it (`GET /repos`).
591
-
592
- **Fields**
593
-
594
- - `key`: `string`
595
-
596
- The cache key: the resource's name, the hostPath leaf, `/repos/<key>`.
597
- - `url`: `string`
598
-
599
- The Binding's own spelling — what the cache clones.
600
- - `identity` (optional): `string`
601
- - `bound`: `boolean`
602
-
603
- A registered Machine binds it — never evicted by `jr2 gc`.
604
- - `lastAttached` (optional): `string`
605
-
606
- When a run last attached it — the eviction clock for a Repo nothing binds. Absent while only
607
- the boot has stated it: a bound Repo no run has attached yet.
608
- - `nodes`: `RepoNodeState[]`
609
-
610
993
  <a id="type-gateview"></a>
611
994
 
612
995
  ### `GateView`
@@ -755,35 +1138,110 @@ Rest in the Agent's state or has left it ([ADR-0024](decisions/0024-an-agents-tu
755
1138
  and its only move was to call again.
756
1139
  - `turnComplete`: `boolean`
757
1140
 
758
- The invoking state stopped waiting: this Agent's turn is over.
759
- - `allowed`: `string[]`
1141
+ The invoking state stopped waiting: this Agent's turn is over.
1142
+ - `allowed`: `string[]`
1143
+
1144
+ The Allowed picks read AFTER the delivery ([ADR-0029](decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)): what this Turn may pick now — narrowed by
1145
+ a pick that moved the Machine within the invoking state, empty once the Turn is over. The
1146
+ Harness renders it ("Allowed now: `finish`"); it does not pre-check against it.
1147
+ - `deliveryId`: `string`
1148
+
1149
+ <a id="type-fetchanswer"></a>
1150
+
1151
+ ### `FetchAnswer`
1152
+
1153
+ ```ts
1154
+ type FetchAnswer = { fetched: string } | { stale: string; asOf: string | null };
1155
+ ```
1156
+
1157
+ What one ask settles as. `fetched` is the landing's timestamp; `stale` is git's own words (or
1158
+ this port's, when the budget ran out) beside the time the cache's objects are as of — `null`
1159
+ when nothing has ever fetched it, which the program prints as "an unknown time".
1160
+
1161
+ **Forms**: it is one of these objects.
1162
+
1163
+ - Form 1: `fetched`
1164
+
1165
+ - `fetched`: `string`
1166
+ - Form 2: `stale`, `asOf`
1167
+
1168
+ - `stale`: `string`
1169
+ - `asOf`: `string | null`
1170
+
1171
+ <a id="type-scopes"></a>
1172
+
1173
+ ### `Scopes`
1174
+
1175
+ ```ts
1176
+ type Scopes = {
1177
+ start?: ScopeGrant;
1178
+ answer?: AnswerGrant;
1179
+ cancel?: ScopeGrant;
1180
+ read?: ScopeGrant;
1181
+ steer?: ScopeGrant;
1182
+ };
1183
+ ```
1184
+
1185
+ What a Caller may do: five scopes, each one for the Workflows that it names ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). A scope
1186
+ that is absent opens nothing. Which scope opens which route is in the HTTP API Reference.
1187
+
1188
+ A Caller's scope does not limit what its run dispatches: a Caller with `start` on `triage` starts
1189
+ a run whose Machine can dispatch `deploy`. The Machine is the author's, and it is the boundary of
1190
+ what its run does.
1191
+
1192
+ **Fields**
1193
+
1194
+ - `start` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
760
1195
 
761
- The Allowed picks read AFTER the delivery ([ADR-0029](decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)): what this Turn may pick now — narrowed by
762
- a pick that moved the Machine within the invoking state, empty once the Turn is over. The
763
- Harness renders it ("Allowed now: `finish`"); it does not pre-check against it.
764
- - `deliveryId`: `string`
1196
+ Start a run.
1197
+ - `answer` (optional): [`AnswerGrant`](config.md#callers-tokens-name-may-answer)
765
1198
 
766
- <a id="type-fetchanswer"></a>
1199
+ Answer a Gate, and read a run's open Gates without its context. A list item
1200
+ `{ workflow, gates }` opens only the Gates that it names, by invoke id (see [`GateGrant`](#type-gategrant)).
1201
+ - `cancel` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
767
1202
 
768
- ### `FetchAnswer`
1203
+ Cancel a run.
1204
+ - `read` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
1205
+
1206
+ Read a run's state: its status, its context and its feed.
1207
+ - `steer` (optional): [`ScopeGrant`](config.md#callers-tokens-name-may-start)
1208
+
1209
+ Send a Steering message to an Agent of a run.
1210
+
1211
+ <a id="type-hookfanin"></a>
1212
+
1213
+ ### `HookFanIn`
769
1214
 
770
1215
  ```ts
771
- type FetchAnswer = { fetched: string } | { stale: string; asOf: string | null };
1216
+ type HookFanIn = {
1217
+ delivered: Array<{ run: string; workflow: string; gate: string; event: string }>;
1218
+ refused: Array<{ run: string; gate: string; event?: string; reason: string }>;
1219
+ ignored: number;
1220
+ sources: SourceLine[];
1221
+ waiting?: string[];
1222
+ replies?: HookReply[];
1223
+ };
772
1224
  ```
773
1225
 
774
- What one ask settles as. `fetched` is the landing's timestamp; `stale` is git's own words (or
775
- this port's, when the budget ran out) beside the time the cache's objects are as of — `null`
776
- when nothing has ever fetched it, which the program prints as "an unknown time".
1226
+ What a Hook's fan-in did ([ADR-0011](decisions/0011-workflow-defined-events.md)): the Gates that took the delivery, the ones that did not and
1227
+ why, and how many open Gates of its kind and key gave nothing.
777
1228
 
778
- **Forms**: it is one of these objects.
1229
+ **Fields**
779
1230
 
780
- - Form 1: `fetched`
1231
+ - `delivered`: `Array<{ run: string; workflow: string; gate: string; event: string; }>`
1232
+ - `refused`: `Array<{ run: string; gate: string; event?: string; reason: string; }>`
1233
+ - `ignored`: `number`
1234
+ - `sources`: `SourceLine[]`
781
1235
 
782
- - `fetched`: `string`
783
- - Form 2: `stale`, `asOf`
1236
+ Each Source's verdict ([ADR-0017](decisions/0017-pool-over-a-source-port.md)), one line for each Pool whose Source hears the kind and key.
1237
+ - `waiting` (optional): `string[]`
784
1238
 
785
- - `stale`: `string`
786
- - `asOf`: `string | null`
1239
+ The standing Workflows whose Source hears the kind and key, and that have no live run and are
1240
+ not paused ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): a hand-over waits out its back-off. The door answers 503.
1241
+ - `replies` (optional): `HookReply[]`
1242
+
1243
+ For a delivery whose answer a person reads: the `reply` of each target that has one, in the
1244
+ order of the fan-out ([ADR-0011](decisions/0011-workflow-defined-events.md)). The door joins them; it never puts them in the outcome.
787
1245
 
788
1246
  <a id="type-chartorigin"></a>
789
1247
 
@@ -880,7 +1338,15 @@ the same child machine are told apart.
880
1338
  ### `DispatchedBy`
881
1339
 
882
1340
  ```ts
883
- type DispatchedBy = { run: string; slot: string; effect: string; depth: number; instance?: string };
1341
+ type DispatchedBy = {
1342
+ run: string;
1343
+ slot: string;
1344
+ effect: string;
1345
+ depth: number;
1346
+ instance?: string;
1347
+ key?: string;
1348
+ hook?: { name: string; delivery: string; onBehalfOf?: string };
1349
+ };
884
1350
  ```
885
1351
 
886
1352
  Which run dispatched a run ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)): the caller's run id, the slot of its `dispatch` Effect, and
@@ -897,25 +1363,145 @@ verify: opaque text, never resolved.
897
1363
  - `effect`: `string`
898
1364
  - `depth`: `number`
899
1365
  - `instance` (optional): `string`
1366
+ - `key` (optional): `string`
1367
+
1368
+ The idempotency key when a Pool of runs started the run ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): the item's id. The store
1369
+ scopes it to the callee Workflow, as it scopes `effect`. Absent: the key is `effect`.
1370
+ - `hook` (optional): `object`
1371
+
1372
+ The Hook delivery that carried the item to the Pool ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): the Hook's name, the delivery
1373
+ id, and the party that the signed payload names. Absent for an item that a read or `next` gave.
1374
+
1375
+ - `name`: `string`
1376
+ - `delivery`: `string`
1377
+ - `onBehalfOf` (optional): `string`
1378
+
1379
+ <a id="type-shownstatus"></a>
1380
+
1381
+ ### `ShownStatus`
1382
+
1383
+ ```ts
1384
+ type ShownStatus = typeof LIVE_STATUS | EndedStatus;
1385
+ ```
1386
+
1387
+ A run status as jr2 shows it, on every route and feed: `LIVE_STATUS` while the run is
1388
+ live, else how it ended. The store's own `live` and xstate's `stopped` are never shown.
1389
+
1390
+ <a id="type-runstarter"></a>
1391
+
1392
+ ### `RunStarter`
1393
+
1394
+ ```ts
1395
+ type RunStarter =
1396
+ | ({ kind: "caller" } & CallerRecord)
1397
+ | { kind: "run"; run: string; slot: string }
1398
+ | { kind: "jr2"; why: "boot" | "publish" | "hand-over" | "resume" };
1399
+ ```
1400
+
1401
+ Who started a run ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): a Caller on a guarded route; a run, through a local `dispatch`
1402
+ ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)); or jr2 itself, for a standing run ([ADR-0017](decisions/0017-pool-over-a-source-port.md)), at `boot`, at a `publish`, at a
1403
+ `hand-over`, or at a `resume`. A run that a foreign `dispatch` started records the Caller whose
1404
+ credential the other Instance sent.
1405
+
1406
+ **Forms**: it is one of these objects.
1407
+
1408
+ - Form 1: `kind`, `via`, `name`, `shown`, `onBehalfOf`
1409
+
1410
+ - `kind`: `"caller"`
1411
+ - `via`: `Caller["via"]`
1412
+ - `name`: `string`
1413
+ - `shown` (optional): `string`
1414
+
1415
+ The name that jr2 shows, when it is not `name` (a JWT's `email`).
1416
+ - `onBehalfOf` (optional): `string`
1417
+
1418
+ The Caller's claim about the party it acts for. jr2 records it and never checks it, except for
1419
+ a Hook, whose signature covers it.
1420
+ - Form 2: `kind`, `run`, `slot`
1421
+
1422
+ - `kind`: `"run"`
1423
+ - `run`: `string`
1424
+ - `slot`: `string`
1425
+ - Form 3: `kind`, `why`
1426
+
1427
+ - `kind`: `"jr2"`
1428
+ - `why`: `"boot" | "publish" | "hand-over" | "resume"`
1429
+
1430
+ <a id="type-reponodestate"></a>
1431
+
1432
+ ### `RepoNodeState`
1433
+
1434
+ ```ts
1435
+ type RepoNodeState = {
1436
+ node: string;
1437
+ present: boolean;
1438
+ synced: boolean;
1439
+ attempted?: "Probe" | "Clone" | "Fetch";
1440
+ lastAttempt?: string;
1441
+ lastFetched?: string;
1442
+ lastError?: string;
1443
+ };
1444
+ ```
1445
+
1446
+ One node's view of a Repo, as the cache agent reports it on the resource's status.
1447
+
1448
+ **Fields**
1449
+
1450
+ - `node`: `string`
1451
+ - `present`: `boolean`
1452
+
1453
+ A clone exists on this node.
1454
+ - `synced`: `boolean`
1455
+
1456
+ The last attempt — a probe, a clone, or a fetch — succeeded.
1457
+ - `attempted` (optional): `"Probe" | "Clone" | "Fetch"`
1458
+
1459
+ Which of the three the last attempt was. A failed Probe is `jr2 status`'s signal for a Repo
1460
+ no pod on the node mounts yet; only a failed Clone fails a Sandbox waiting on that node.
1461
+ - `lastAttempt` (optional): `string`
1462
+ - `lastFetched` (optional): `string`
1463
+
1464
+ The last successful clone or fetch.
1465
+ - `lastError` (optional): `string`
1466
+
1467
+ git's own words; absent when synced.
900
1468
 
901
1469
  <a id="type-runrecord"></a>
902
1470
 
903
1471
  ### `RunRecord`
904
1472
 
905
1473
  ```ts
906
- type RunRecord = { runId: string; workflow: string; instanceId: string; dispatchedBy?: DispatchedBy };
1474
+ type RunRecord = {
1475
+ runId: string;
1476
+ workflow: string;
1477
+ instanceId: string;
1478
+ startedAt: string;
1479
+ dispatchedBy?: DispatchedBy;
1480
+ startedBy?: RunStarter;
1481
+ resumedBy?: CallerRecord;
1482
+ };
907
1483
  ```
908
1484
 
909
1485
  The serializable identity of a run — what reconcile sees and what restore rebuilds from.
910
1486
  `dispatchedBy` is the run that started it through a `dispatch` Effect, and this run's depth in
911
1487
  that chain, as the caller sent it ([ADR-0071](decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md)); absent for a run a human or a client started.
1488
+ `startedBy` is who started it ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): a Caller, a run, or jr2. Behind the token, like
1489
+ `context`: `observe` never carries it. Absent for a run that a host started with no
1490
+ starter, as a test does.
912
1491
 
913
1492
  **Fields**
914
1493
 
915
1494
  - `runId`: `string`
916
1495
  - `workflow`: `string`
917
1496
  - `instanceId`: `string`
1497
+ - `startedAt`: `string`
1498
+
1499
+ When the run started, ISO-8601 ([ADR-0009](decisions/0009-cli-and-instance-interface.md)): the order of `GET /runs`.
918
1500
  - `dispatchedBy` (optional): [`DispatchedBy`](#type-dispatchedby)
1501
+ - `startedBy` (optional): [`RunStarter`](#type-runstarter)
1502
+ - `resumedBy` (optional): [`CallerRecord`](authoring-surface/type-callerrecord.md)
1503
+
1504
+ The Caller whose `jr2 run` resumed this standing run ([ADR-0017](decisions/0017-pool-over-a-source-port.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)).
919
1505
 
920
1506
  <a id="type-runwaiting"></a>
921
1507
 
@@ -973,61 +1559,97 @@ ledger row was written.
973
1559
  - `run`: `string`
974
1560
  - `at`: `string`
975
1561
 
976
- <a id="type-runworktree"></a>
1562
+ <a id="type-callerrecord"></a>
977
1563
 
978
- ### `RunWorktree`
1564
+ ### `CallerRecord`
979
1565
 
980
1566
  ```ts
981
- type RunWorktree = { slot: string; path: string; branch: string | null };
1567
+ type CallerRecord = {
1568
+ via: Caller["via"];
1569
+ name: string;
1570
+ shown?: string;
1571
+ onBehalfOf?: string;
1572
+ };
982
1573
  ```
983
1574
 
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.
1575
+ A Caller as a run records it ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): who acted, and the party it says it acted for. Never its
1576
+ scopes.
986
1577
 
987
1578
  **Fields**
988
1579
 
989
- - `slot`: `string`
990
- - `path`: `string`
991
- - `branch`: `string | null`
1580
+ - `via`: `Caller["via"]`
1581
+ - `name`: `string`
1582
+ - `shown` (optional): `string`
992
1583
 
993
- <a id="type-reponodestate"></a>
1584
+ The name that jr2 shows, when it is not `name` (a JWT's `email`).
1585
+ - `onBehalfOf` (optional): `string`
994
1586
 
995
- ### `RepoNodeState`
1587
+ The Caller's claim about the party it acts for. jr2 records it and never checks it, except for
1588
+ a Hook, whose signature covers it.
1589
+
1590
+ <a id="type-gateanswer"></a>
1591
+
1592
+ ### `GateAnswer`
996
1593
 
997
1594
  ```ts
998
- type RepoNodeState = {
999
- node: string;
1000
- present: boolean;
1001
- synced: boolean;
1002
- attempted?: "Probe" | "Clone" | "Fetch";
1003
- lastAttempt?: string;
1004
- lastFetched?: string;
1005
- lastError?: string;
1595
+ type GateAnswer = { gate: string; type: string; at: string; answeredBy: CallerRecord; delivery?: string };
1596
+ ```
1597
+
1598
+ One answer to a Gate of a run, and the Caller that gave it ([ADR-0011](decisions/0011-workflow-defined-events.md), [ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). `delivery` is
1599
+ the Hook's delivery id when a Hook gave it: the run's `(delivery, gate)` pairs, so a restore never
1600
+ gives one delivery twice to one Gate.
1601
+
1602
+ **Fields**
1603
+
1604
+ - `gate`: `string`
1605
+ - `type`: `string`
1606
+ - `at`: `string`
1607
+ - `answeredBy`: [`CallerRecord`](authoring-surface/type-callerrecord.md)
1608
+ - `delivery` (optional): `string`
1609
+
1610
+ <a id="type-steeringrecord"></a>
1611
+
1612
+ ### `SteeringRecord`
1613
+
1614
+ ```ts
1615
+ type SteeringRecord = {
1616
+ id: string;
1617
+ agent: string;
1618
+ author: SteeringAuthor;
1619
+ text: string;
1620
+ at: string;
1621
+ outcome: SteeringOutcome;
1006
1622
  };
1007
1623
  ```
1008
1624
 
1009
- One node's view of a Repo, as the cache agent reports it on the resource's status.
1625
+ The run's record of one Steering message: the slot it went to, its author, its text, when it was
1626
+ sent, and what became of it.
1010
1627
 
1011
1628
  **Fields**
1012
1629
 
1013
- - `node`: `string`
1014
- - `present`: `boolean`
1630
+ - `id`: `string`
1631
+ - `agent`: `string`
1632
+ - `author`: [`SteeringAuthor`](#type-steeringauthor)
1633
+ - `text`: `string`
1634
+ - `at`: `string`
1635
+ - `outcome`: [`SteeringOutcome`](#type-steeringoutcome)
1015
1636
 
1016
- A clone exists on this node.
1017
- - `synced`: `boolean`
1637
+ <a id="type-runworktree"></a>
1018
1638
 
1019
- The last attempt — a probe, a clone, or a fetch — succeeded.
1020
- - `attempted` (optional): `"Probe" | "Clone" | "Fetch"`
1639
+ ### `RunWorktree`
1021
1640
 
1022
- Which of the three the last attempt was. A failed Probe is `jr2 status`'s signal for a Repo
1023
- no pod on the node mounts yet; only a failed Clone fails a Sandbox waiting on that node.
1024
- - `lastAttempt` (optional): `string`
1025
- - `lastFetched` (optional): `string`
1641
+ ```ts
1642
+ type RunWorktree = { slot: string; path: string; branch: string | null };
1643
+ ```
1026
1644
 
1027
- The last successful clone or fetch.
1028
- - `lastError` (optional): `string`
1645
+ 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
1646
+ branch of the cut. `branch` is `null` for a detached Worktree.
1029
1647
 
1030
- git's own words; absent when synced.
1648
+ **Fields**
1649
+
1650
+ - `slot`: `string`
1651
+ - `path`: `string`
1652
+ - `branch`: `string | null`
1031
1653
 
1032
1654
  <a id="type-surfaceevent"></a>
1033
1655
 
@@ -1094,6 +1716,80 @@ whether work ran. Plain data; the Harness writes the words. `answer`: the value
1094
1716
  - `kind`: `"answer"`
1095
1717
  - `value`: `unknown`
1096
1718
 
1719
+ <a id="type-scopegrant"></a>
1720
+
1721
+ ### `ScopeGrant`
1722
+
1723
+ ```ts
1724
+ type ScopeGrant = "all" | readonly string[];
1725
+ ```
1726
+
1727
+ The Workflows that one scope of a Caller opens ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): `"all"` for every Workflow, or a list
1728
+ of Workflow names. A Chart wrapper's name in the list also opens each named Chart of that wrapper
1729
+ (`plan` opens `plan/weekly`), because a named Chart runs its wrapper's Machine. A named Chart's
1730
+ own name (`plan/weekly`) opens that Chart alone.
1731
+
1732
+ <a id="type-answergrant"></a>
1733
+
1734
+ ### `AnswerGrant`
1735
+
1736
+ ```ts
1737
+ type AnswerGrant = "all" | readonly (string | GateGrant)[];
1738
+ ```
1739
+
1740
+ The Gates that the `answer` scope opens ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): `"all"`, or a list in which each item is a
1741
+ Workflow name, which opens every Gate of that Workflow, or a [`GateGrant`](#type-gategrant), which opens only
1742
+ the Gates that it names: `["triage", { workflow: "deploy", gates: ["approve"] }]`.
1743
+
1744
+ <a id="type-sourceline"></a>
1745
+
1746
+ ### `SourceLine`
1747
+
1748
+ ```ts
1749
+ type SourceLine = { pool: string; workflow: string; source: string } & SourceVerdictLine;
1750
+ ```
1751
+
1752
+ One Source's verdict on one delivery ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): the Pool's run, its Workflow, the `source`
1753
+ Gate's id, and the verdict.
1754
+
1755
+ **Fields**: each field of [`SourceVerdictLine`](#type-sourceverdictline), and these:
1756
+
1757
+ - `pool`: `string`
1758
+ - `workflow`: `string`
1759
+ - `source`: `string`
1760
+
1761
+ <a id="type-hookreply"></a>
1762
+
1763
+ ### `HookReply`
1764
+
1765
+ ```ts
1766
+ type HookReply = {
1767
+ target: "source" | "gate";
1768
+ workflow: string;
1769
+ run: string;
1770
+ gate: string;
1771
+ text?: string;
1772
+ skipped?: true;
1773
+ error?: string;
1774
+ };
1775
+ ```
1776
+
1777
+ What one target's `reply` gave ([ADR-0011](decisions/0011-workflow-defined-events.md)): its place in the join (a Source or a Gate, its
1778
+ Workflow and its run), and its text, or the error of a `reply` that threw or gave no text. A
1779
+ `skipped` Source's text shows only when no other target gave one.
1780
+
1781
+ **Fields**
1782
+
1783
+ - `target`: `"source" | "gate"`
1784
+ - `workflow`: `string`
1785
+ - `run`: `string`
1786
+ - `gate`: `string`
1787
+
1788
+ The Gate's id, or the Pool's `source` Gate's id.
1789
+ - `text` (optional): `string`
1790
+ - `skipped` (optional): `true`
1791
+ - `error` (optional): `string`
1792
+
1097
1793
  <a id="type-machinestatedoc"></a>
1098
1794
 
1099
1795
  ### `MachineStateDoc`
@@ -1240,6 +1936,57 @@ input names `accepts:` itself.
1240
1936
  - `agent` (optional): `string`
1241
1937
  - `names`: `string[]`
1242
1938
 
1939
+ <a id="type-endedstatus"></a>
1940
+
1941
+ ### `EndedStatus`
1942
+
1943
+ ```ts
1944
+ type EndedStatus = "done" | "failed" | "error" | "cancelled" | "lost" | "drifted";
1945
+ ```
1946
+
1947
+ How a run ended, as the store writes it ([ADR-0065](decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md), [ADR-0025](decisions/0025-cancel-ends-a-run-stop-parks-it.md), [ADR-0030](decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md), [ADR-0073](decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md), [ADR-0074](decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md)).
1948
+
1949
+ <a id="type-steeringauthor"></a>
1950
+
1951
+ ### `SteeringAuthor`
1952
+
1953
+ ```ts
1954
+ type SteeringAuthor =
1955
+ | { kind: "caller"; via: "token" | "jwt" | "hook"; name: string; onBehalfOf?: string }
1956
+ | { kind: "machine"; from?: { party: "agent" | "gate"; id: string } };
1957
+ ```
1958
+
1959
+ Who wrote a Steering message. `caller`: a Caller sent it from outside the run ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)), with
1960
+ the name jr2 shows and the party it says it acts for. `machine`: the Machine sent it with its
1961
+ `steer` action; `from` is the party whose words it relays, when it relays some.
1962
+
1963
+ **Forms**: it is one of these objects.
1964
+
1965
+ - Form 1: `kind`, `via`, `name`, `onBehalfOf`
1966
+
1967
+ - `kind`: `"caller"`
1968
+ - `via`: `"token" | "jwt" | "hook"`
1969
+ - `name`: `string`
1970
+ - `onBehalfOf` (optional): `string`
1971
+ - Form 2: `kind`, `from`
1972
+
1973
+ - `kind`: `"machine"`
1974
+ - `from` (optional): `object`
1975
+
1976
+ - `party`: `"agent" | "gate"`
1977
+ - `id`: `string`
1978
+
1979
+ <a id="type-steeringoutcome"></a>
1980
+
1981
+ ### `SteeringOutcome`
1982
+
1983
+ ```ts
1984
+ type SteeringOutcome = "held" | "heard" | "not-delivered";
1985
+ ```
1986
+
1987
+ What became of one Steering message ([ADR-0002](decisions/0002-duplex-streaming-actor.md)): `held` until a Turn of its slot hears it,
1988
+ `heard` once one did, `not-delivered` when its Machine instance ended first.
1989
+
1243
1990
  <a id="type-eventsemantics"></a>
1244
1991
 
1245
1992
  ### `EventSemantics`
@@ -1262,6 +2009,58 @@ How the party that sends an event gets its answer.
1262
2009
  - [The Menu](../concepts/the-menu.md)
1263
2010
  - [ADR-0006: The agent picks from a Machine-defined, schema-backed menu; it does not drive the workflow](decisions/0006-agent-control-surface.md)
1264
2011
 
2012
+ <a id="type-gategrant"></a>
2013
+
2014
+ ### `GateGrant`
2015
+
2016
+ ```ts
2017
+ type GateGrant = {
2018
+ workflow: string;
2019
+ gates: readonly string[];
2020
+ };
2021
+ ```
2022
+
2023
+ Some Gates of one Workflow, in the `answer` scope ([ADR-0078](decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). A Gate's name is its invoke id: the
2024
+ static name that it has in the Machine that invokes it, and the id that its events carry as their
2025
+ sender ([ADR-0011](decisions/0011-workflow-defined-events.md)). That is the invoke `id` that the author wrote, or else the key path of its
2026
+ gated state below the root of that Machine (`body.humanReview`). One name opens every Gate of the
2027
+ Workflow with that id, in each child of a Pool too.
2028
+
2029
+ `jr2 up` refuses an item for a Workflow that the Assembly does not have, a name that no Machine
2030
+ of the Workflow has, and a name that Gates in more than one Machine of the Workflow have. A Chart
2031
+ wrapper's item (`{ workflow: "plan", gates }`) opens those Gates in each Chart of `plan` too, and
2032
+ `jr2 up` checks its names against the wrapper's Machines and its Chart files. A runtime Chart is
2033
+ not in the Assembly, so `jr2 up` cannot check its Gates: a name that no Machine and no Chart file
2034
+ of the wrapper has, and an item for a runtime Chart (`plan/weekly` with no Chart file), are
2035
+ warnings, not refusals.
2036
+
2037
+ **Fields**
2038
+
2039
+ - `workflow`: `string`
2040
+
2041
+ The Workflow, as a bare name in the list names it.
2042
+ - `gates`: `readonly string[]`
2043
+
2044
+ The Gates' names.
2045
+
2046
+ <a id="type-sourceverdictline"></a>
2047
+
2048
+ ### `SourceVerdictLine`
2049
+
2050
+ ```ts
2051
+ type SourceVerdictLine = { verdict: SourceVerdictName; key?: string; run?: string; reason?: string };
2052
+ ```
2053
+
2054
+ A Source's verdict on one delivery ([ADR-0017](decisions/0017-pool-over-a-source-port.md)): `key` for a carried item, `run` when a run holds
2055
+ that key, `reason` for a skip.
2056
+
2057
+ **Fields**
2058
+
2059
+ - `verdict`: [`SourceVerdictName`](#type-sourceverdictname)
2060
+ - `key` (optional): `string`
2061
+ - `run` (optional): `string`
2062
+ - `reason` (optional): `string`
2063
+
1265
2064
  <a id="type-effectdoc"></a>
1266
2065
 
1267
2066
  ### `EffectDoc`
@@ -1279,7 +2078,17 @@ type EffectDoc =
1279
2078
  file?: true;
1280
2079
  }
1281
2080
  | { kind: "push"; sign: boolean; squash: boolean; force: boolean; pin: boolean; timeoutMs: number; slot?: string }
1282
- | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number };
2081
+ | { kind: "dispatch"; workflow: string; wait: boolean; target?: string; timeoutMs: number }
2082
+ | {
2083
+ kind: "mcp";
2084
+ op: "call" | "read" | "prompt";
2085
+ server: string;
2086
+ tool?: string;
2087
+ uri?: string;
2088
+ prompt?: string;
2089
+ timeoutMs?: number;
2090
+ idempotent: boolean;
2091
+ };
1283
2092
  ```
1284
2093
 
1285
2094
  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
@@ -1319,6 +2128,16 @@ An Effect slot's parts, as the doc carries them ([ADR-0067](decisions/0067-an-ef
1319
2128
  - `wait`: `boolean`
1320
2129
  - `target` (optional): `string`
1321
2130
  - `timeoutMs`: `number`
2131
+ - Form 5: `kind`, `op`, `server`, `tool`, `uri`, `prompt`, `timeoutMs`, `idempotent`
2132
+
2133
+ - `kind`: `"mcp"`
2134
+ - `op`: `"call" | "read" | "prompt"`
2135
+ - `server`: `string`
2136
+ - `tool` (optional): `string`
2137
+ - `uri` (optional): `string`
2138
+ - `prompt` (optional): `string`
2139
+ - `timeoutMs` (optional): `number`
2140
+ - `idempotent`: `boolean`
1322
2141
 
1323
2142
  <a id="type-childmachinedoc"></a>
1324
2143
 
@@ -1384,3 +2203,11 @@ event to the other party too (`"any"`). Or set it to keep an event on the root f
1384
2203
  - [The Menu](../concepts/the-menu.md)
1385
2204
  - [Gates](../concepts/gates.md)
1386
2205
  - [ADR-0015: Workflows are authored through `jr2Setup`; vocabulary and agent menus derive from the machine](decisions/0015-authoring-surface-absorbs-the-mechanism.md)
2206
+
2207
+ <a id="type-sourceverdictname"></a>
2208
+
2209
+ ### `SourceVerdictName`
2210
+
2211
+ ```ts
2212
+ type SourceVerdictName = (typeof SOURCE_VERDICTS)[number];
2213
+ ```