@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
@@ -86,7 +86,7 @@ jr2 up — instance "my-instance" → context kind-jr2 / namespace my-instance
86
86
  typecheck: tsc --noEmit (the instance's own compiler)
87
87
  agents: (none) (carried by 1 workflow machine(s))
88
88
  assembly: staging
89
- assembly: ba71fa27f37f (kit v<version>; 1 workflow(s): ping)
89
+ assembly: 50e6f81e3bfb (kit v<version>; 1 workflow(s): ping)
90
90
  kubernetes: v1.36.1 (floor 1.35, ADR-0037)
91
91
  first contact: deploy instance "my-instance" to context kind-jr2 (create namespace "my-instance")? [y/N] y
92
92
  platforms: linux/amd64 (from 1 schedulable node(s))
@@ -94,14 +94,15 @@ images: installed kit — the published v<version> Harness, operator and Orchest
94
94
  …
95
95
  orchestrator: waiting for rollout
96
96
  orchestrator: verified — 1 pod(s) running ghcr.io/snapwich/jr2-orchestrator:<version>
97
- instance effector: none (no Effect is placed there)
98
- assembly: publishing ba71fa27f37f (no current Assembly)
99
- assembly: ba71fa27f37f is current — workflows: ping
97
+ instance effector: none (no Effect is placed there, and no Hook needs it)
98
+ assembly: publishing 50e6f81e3bfb (no current Assembly)
99
+ assembly: 50e6f81e3bfb is current — workflows: ping
100
100
  swept nothing — every labeled image is named by a live root
101
+ …
101
102
  converged — `jr2 run <workflow>` when ready
102
103
  ```
103
104
 
104
- `<version>` is the Kit version, such as `0.4.1`. `ba71fa27f37f` is the hash of the
105
+ `<version>` is the Kit version, such as `0.4.1`. `50e6f81e3bfb` is the hash of the
105
106
  [Assembly](../reference/glossary.md#assembly) of your Instance: your Workflows and your config. Your hash can be
106
107
  different. It changes each time that your Workflows or your config change.
107
108
 
@@ -122,9 +123,9 @@ JSON:
122
123
 
123
124
  ```text
124
125
  → context kind-jr2 / namespace my-instance
125
- run a256ff8a-f99f-42f5-975c-4f93a5008c56 (ping) — attached
126
+ run db1fd16f-166f-4575-b8fb-60cd80321308 (ping) — attached
126
127
  → done "done"
127
- {"runId":"a256ff8a-f99f-42f5-975c-4f93a5008c56","workflow":"ping","instanceId":"0e82cc83-7c15-426f-86ff-e7354873a429","status":"done","value":"done","context":{"message":"hi","reply":"pong: hi"},"children":[],"waiting":[],"held":[],"assembly":"ba71fa27f37f"}
128
+ {"runId":"db1fd16f-166f-4575-b8fb-60cd80321308","workflow":"ping","instanceId":"71af068c-e150-4802-8b1c-6d07d3bb4a08","startedAt":"2026-11-06T19:15:47.436Z","status":"done","value":"done","context":{"message":"hi","reply":"pong: hi"},"children":[],"waiting":[],"held":[],"assembly":"50e6f81e3bfb","startedBy":{"kind":"caller","via":"token","name":"instance","onBehalfOf":"kubernetes-admin"}}
128
129
  ```
129
130
 
130
131
  Your run id and your [Instance ID](../reference/glossary.md#instance-id) are not the same as the ones here. The run is
@@ -155,16 +156,17 @@ Click `ping`. The center shows its Machine, with the states `responding` and `do
155
156
  ## Unlock the Console
156
157
 
157
158
  Without a token, you can look at the runs in the Console, but you cannot start a run or answer a
158
- [Gate](../reference/glossary.md#gate). For these, you need the Instance token. In the first terminal, run:
159
+ [Gate](../reference/glossary.md#gate). For these, you need the token of a Caller with a scope. Here, use the Instance
160
+ token. In the first terminal, run:
159
161
 
160
162
  ```sh
161
163
  kubectl -n my-instance get secret jr2-instance -o jsonpath='{.data.JR2_INSTANCE_TOKEN}' | base64 -d
162
164
  ```
163
165
 
164
- The command prints the token, 48 hexadecimal characters, with no newline after it. Paste the token in the
165
- `instance token` box in the Console nav. Push Enter. The Console checks the token. Then the badge after the box shows
166
- `live`, and a `start` button shows next to `ping`. The browser tab keeps the token until you close the tab. `jr2 up`
167
- keeps the same token each time that it runs.
166
+ The command prints the token, 48 hexadecimal characters, with no newline after it. Paste the token in the `Caller token`
167
+ box in the Console nav. Push Enter. The Console checks the token. Then the badge after the box shows `live`, the nav
168
+ shows `as token/instance`, and a `start` button shows next to `ping`. The browser tab keeps the token until you close
169
+ the tab. `jr2 up` keeps the same token each time that it runs.
168
170
 
169
171
  Keep the port forward open. The next pages use the Console. [Open the Console](../guides/open-the-console.md) gives more
170
172
  about the Console.
@@ -181,8 +183,9 @@ each time that you change the folder. [Converge](../concepts/converge.md) explai
181
183
  it. [Machines and Workflows](../concepts/machines-and-workflows.md) explains the two words.
182
184
 
183
185
  The Console is a page that the Orchestrator serves, at [`GET /`](../reference/http-api.md#get). Anyone who can reach it
184
- can see the runs. Only the holder of the Instance token can start a run or answer a Gate
185
- ([ADR-0032](../reference/decisions/0032-the-console-unlocks-with-the-instance-token.md)).
186
+ can see the runs. Only a [Caller](../reference/glossary.md#caller) with a scope, such as the holder of the Instance
187
+ token, can start a run or answer a Gate
188
+ ([ADR-0032](../reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md)).
186
189
 
187
190
  `jr2 init` wrote `AGENTS.md` for your Assistant. It points to the docs that the Kit installs in
188
191
  `node_modules/@jr2/orchestrator/docs/`. These are the docs of your Kit version
@@ -12,16 +12,17 @@ Put the programs that your [Agents](../reference/glossary.md#agent) and
12
12
  A [Workspace](../reference/glossary.md#workspace) runs its body in a [Sandbox](../reference/glossary.md#sandbox): a pod
13
13
  of its own. The Sandbox Image is the image of the pod's `harness` container. The Agent's
14
14
  [Working tools](../reference/glossary.md#working-tools) run in that container, and so do the `command` Effects and the
15
- shell that you get with `kubectl exec`. Thus a program that is in the image is a program that they can run.
15
+ shell that you get with `jr2 workspace shell`. Thus a program that is in the image is a program that they can run.
16
16
 
17
17
  Use this table to choose:
18
18
 
19
- | You want | Do this |
20
- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
21
- | The same tools for each Workspace | Change `images/default/Dockerfile`. |
22
- | Different tools for the Workspaces of one file | Put a docker context beside the workflow module, and name it in the `image` option. |
23
- | An image that you build and push yourself | Write its registry ref in the `image` option. |
24
- | A service that runs beside the Agent | Name its image in the `user` option. It runs in the [User Container](../reference/glossary.md#user-container). |
19
+ | You want | Do this |
20
+ | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
21
+ | The same tools for each Workspace | Change `images/default/Dockerfile`. |
22
+ | Different tools for the Workspaces of one file | Put a docker context beside the workflow module, and name it in the `image` option. |
23
+ | An image that you build and push yourself | Write its registry ref in the `image` option. |
24
+ | A service that runs beside the Agent | Name its image in the `user` option. It runs in the [User Container](../reference/glossary.md#user-container). |
25
+ | A tool that the model calls by name | Put an MCP server in the image, and declare it in `jr2.config.ts`. See [Write a tool as an MCP server](write-a-tool-as-an-mcp-server.md). |
25
26
 
26
27
  ## Add a tool to the default image
27
28
 
@@ -239,17 +240,23 @@ RUN apk add --no-cache git
239
240
  # The Worktrees under /work belong to a different user. Tell git to trust them.
240
241
  RUN git config --system --add safe.directory '*'
241
242
 
242
- # jr2 does not replace this command. It must keep running: when it ends, the pod is not ready.
243
+ # jr2 does not replace this command. It must keep running: when it ends, Kubernetes starts the container again.
243
244
  CMD ["sh", "-c", "while true; do sleep 3600; done"]
244
245
  ```
245
246
 
246
247
  Save it as `workflows/shell/Dockerfile`. Name it in the `workspace()` options with
247
248
  `user: import.meta.resolve("./shell")`. `user` takes the same two forms as `image`, and `jr2 up` builds a `file:`
248
- context in the same way. Open the shell with `kubectl -n <namespace> exec -it <sandbox> -c user -- sh`.
249
+ context in the same way. Open the shell with [`jr2 workspace shell`](../reference/cli.md#workspace-shell) and `--user`.
250
+ [Look inside a Sandbox](look-inside-a-sandbox.md#find-the-sandbox) tells how to find `<workspace>`:
251
+
252
+ ```sh
253
+ jr2 workspace shell <workspace> --user
254
+ ```
249
255
 
250
256
  The User Container is not a second Sandbox Image:
251
257
 
252
- - Its own `ENTRYPOINT` and `CMD` run. Its process must keep running: when it ends, the pod is not ready.
258
+ - Its own `ENTRYPOINT` and `CMD` run. Its process must keep running. When it ends, Kubernetes starts the container
259
+ again, so a `CMD` that ends at once restarts again and again.
253
260
  - jr2 gives it `/work`, which it can write, and `/repos` and `/opt/jr2`, which it can only read. jr2 gives it no other
254
261
  mount, no environment variable and no command.
255
262
  - It has no floor: an Alpine base is correct here. It runs as the `USER` of its image, and root is permitted.
@@ -266,6 +273,42 @@ container, the [Sandbox Effector](../reference/glossary.md#sandbox-effector). By
266
273
  [Run a command or call a URL with an Effect](run-a-command-or-call-a-url.md) and
267
274
  [Push and sign a branch](push-and-sign-a-branch.md).
268
275
 
276
+ ### Reach a service in the User Container
277
+
278
+ No Service of the cluster goes to the User Container. To reach a port in the Sandbox pod, run
279
+ [`jr2 workspace forward`](../reference/cli.md#workspace-forward). It forwards a port on your computer to the port in the
280
+ pod, through the API server of the cluster, until you push Ctrl-C:
281
+
282
+ ```sh
283
+ jr2 workspace forward <workspace> 3000
284
+ ```
285
+
286
+ ```text
287
+ → context <context> / namespace <namespace>
288
+ 127.0.0.1:36779
289
+ forwarding 127.0.0.1:36779 → 7a0e8b50/x:0:3000 — Ctrl-C ends it
290
+ ```
291
+
292
+ The address on stdout is the local address. The port changes on each run.
293
+
294
+ When the User Container runs an sshd on port `22`, `ssh` can go through `jr2 workspace forward --stdio`. Add this entry
295
+ to `~/.ssh/config`. Replace `<namespace>` with the namespace of your Instance:
296
+
297
+ ```text
298
+ Host *.jr2
299
+ ProxyCommand jr2 workspace forward %h 22 --stdio -n <namespace>
300
+ ```
301
+
302
+ Then connect with the name of the Sandbox, which `jr2 workspace ls` prints, and `.jr2`:
303
+
304
+ ```sh
305
+ ssh <sandbox>.jr2
306
+ ```
307
+
308
+ With `-n`, `jr2 workspace forward` needs no Instance folder, so `ssh` can run it from each folder. The sshd and its keys
309
+ are yours: jr2 gives the User Container no key. With `ssh -A`, your `ssh-agent` goes only to the User Container, and the
310
+ Agent runs nothing there.
311
+
269
312
  ## Rules
270
313
 
271
314
  - `workspace()` refuses an `image` that is empty or not a string:
@@ -303,6 +346,7 @@ container, the [Sandbox Effector](../reference/glossary.md#sandbox-effector). By
303
346
  [`UserContainer`](../reference/authoring-surface/type-usercontainer.md), [`jr2 up`](../reference/cli.md#up)
304
347
  - [ADR-0037](../reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md): the Instance
305
348
  builds its Sandbox Images, and jr2 injects the Harness.
306
- - [ADR-0005](../reference/decisions/0005-sandbox-pod-composition.md): the containers of the Sandbox pod.
349
+ - [ADR-0005](../reference/decisions/0005-sandbox-pod-composition.md): the containers of the Sandbox pod, and how you
350
+ reach the User Container.
307
351
  - [ADR-0049](../reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md): a Machine carries its
308
352
  images.
@@ -0,0 +1,304 @@
1
+ ---
2
+ title: Answer a Gate from a forge
3
+ description: Make a Gate that a review on GitHub or GitLab answers, and that looks for the review it waits for.
4
+ ---
5
+
6
+ # Answer a Gate from a forge
7
+
8
+ Make a [Gate](../reference/glossary.md#gate) that a review on a forge, such as GitHub, answers through a
9
+ [Hook](../reference/glossary.md#hook), and that looks for the review when no delivery comes.
10
+
11
+ A Gate slot is a Gate that hears a Hook. You declare it with `gate({ … })` in `actors`, and a state invokes it by its
12
+ key. It is the same Gate as `src: "gate"`: it has the same id, the same events, the same card in the
13
+ [Console](../reference/glossary.md#console), and `jr2 send` can answer it too. It adds four parts:
14
+
15
+ - `meta`: a zod object of the data that the state gives the Gate. The Gate checks the data when it opens.
16
+ - `delivers`: the events that the Gate can give from a delivery or from its look.
17
+ - `hooks`: a handler for each [Hook kind](../reference/glossary.md#hook-kind) and event key. A handler reads the payload
18
+ of a delivery, and gives one event of `delivers`, or `undefined` when the delivery is not for this Gate.
19
+ - `look`: a read of the fact that the Gate waits for, with an `http` Effect that is marked `idempotent: true`.
20
+
21
+ The example is the [Workflow](../reference/glossary.md#workflow) `review`. A run waits for a review of one commit of one
22
+ pull request. The `github` kind is `@jr2/orchestrator/hooks/github`, so your
23
+ [Instance](../reference/glossary.md#instance) installs nothing more.
24
+
25
+ Save the file as `workflows/review.ts`:
26
+
27
+ ```ts
28
+ // workflows/review.ts: a run that waits for a review of one pull request on GitHub. A review on
29
+ // GitHub answers the Gate, through a Hook of the `github` kind. The look reads the reviews of the
30
+ // pull request, so a review that came before the Gate opened, or while the Orchestrator was down,
31
+ // also answers it.
32
+
33
+ import { z } from "zod";
34
+ import type {} from "@jr2/orchestrator/hooks/github";
35
+ import { bearer, defineEvent, gate, http, jr2Setup, secret, type HttpOutput } from "@jr2/orchestrator";
36
+
37
+ const approve = defineEvent({
38
+ name: "approve",
39
+ description: "A reviewer approved the commit.",
40
+ input: z.object({ by: z.string() }),
41
+ });
42
+ const changes = defineEvent({
43
+ name: "changes",
44
+ description: "A reviewer asked for changes.",
45
+ input: z.object({ by: z.string(), notes: z.string() }),
46
+ });
47
+
48
+ type Meta = { owner: string; repo: string; pr: number; url: string; sha: string };
49
+
50
+ // The reviews of the pull request, from the GitHub API. The look reads them again, so the call is
51
+ // marked idempotent.
52
+ const reviews = http({
53
+ method: "GET",
54
+ url: "https://api.github.com",
55
+ auth: bearer(secret("github-token")),
56
+ headers: { accept: "application/vnd.github+json" },
57
+ idempotent: true,
58
+ input: z.object({ owner: z.string(), repo: z.string(), pr: z.number() }),
59
+ request: ({ owner, repo, pr }) => ({ path: ["repos", owner, repo, "pulls", String(pr), "reviews"] }),
60
+ });
61
+
62
+ /** The last verdict on the commit, from the API's answer. */
63
+ function verdictOf(output: HttpOutput, meta: Meta) {
64
+ if (output.outcome !== "response" || output.status !== 200 || output.body === undefined) return undefined;
65
+ const list = JSON.parse(output.body) as Array<{
66
+ state: string;
67
+ commit_id: string;
68
+ body: string | null;
69
+ user: { login: string };
70
+ }>;
71
+ const last = list.filter((r) => r.commit_id === meta.sha && r.state !== "COMMENTED").at(-1);
72
+ if (last?.state === "APPROVED") return { type: "approve" as const, by: last.user.login };
73
+ if (last?.state === "CHANGES_REQUESTED")
74
+ return { type: "changes" as const, by: last.user.login, notes: last.body ?? "" };
75
+ return undefined;
76
+ }
77
+
78
+ const review = gate({
79
+ meta: z.object({ owner: z.string(), repo: z.string(), pr: z.number(), url: z.string(), sha: z.string() }),
80
+ delivers: [approve, changes],
81
+ hooks: {
82
+ github: {
83
+ "pull_request_review.submitted": ({ payload, meta }) => {
84
+ // A review of another pull request, or of an older commit, is not for this Gate.
85
+ if (payload.pull_request.html_url !== meta.url || payload.review.commit_id !== meta.sha) return undefined;
86
+ if (payload.review.state === "approved") return { type: "approve", by: payload.review.user.login };
87
+ if (payload.review.state === "changes_requested") {
88
+ return { type: "changes", by: payload.review.user.login, notes: payload.review.body ?? "" };
89
+ }
90
+ return undefined;
91
+ },
92
+ },
93
+ },
94
+ look: {
95
+ read: reviews,
96
+ input: (meta) => ({ owner: meta.owner, repo: meta.repo, pr: meta.pr }),
97
+ every: "10m",
98
+ until: verdictOf,
99
+ },
100
+ });
101
+
102
+ type Input = Meta;
103
+
104
+ export const machine = jr2Setup({
105
+ types: {} as { context: Input & { notes?: string }; input: Input },
106
+ events: [approve, changes],
107
+ actors: { review },
108
+ }).createMachine({
109
+ id: "review",
110
+ input: z.object({ owner: z.string(), repo: z.string(), pr: z.number(), url: z.string(), sha: z.string() }),
111
+ context: ({ input }) => input,
112
+ initial: "inReview",
113
+ states: {
114
+ inReview: {
115
+ invoke: {
116
+ src: "review",
117
+ input: ({ context }) => ({
118
+ meta: { owner: context.owner, repo: context.repo, pr: context.pr, url: context.url, sha: context.sha },
119
+ }),
120
+ },
121
+ on: { approve: "approved", changes: "changesAsked" },
122
+ },
123
+ approved: { type: "final" },
124
+ changesAsked: { type: "final" },
125
+ },
126
+ });
127
+ ```
128
+
129
+ The `import type {} from "@jr2/orchestrator/hooks/github"` line gives the handlers their types: the event key
130
+ `pull_request_review.submitted` and its payload. A key that no kind in scope gives does not compile. A handler reads
131
+ only the fields that the kind's schema names.
132
+
133
+ ## The handler
134
+
135
+ A delivery of the kind and the event key goes to each open Gate whose handlers hear them. Thus each handler compares the
136
+ payload with the Gate's `meta`. In the example, the handler gives `undefined` for a review of another pull request or of
137
+ an older commit. A handler is pure and synchronous: it does no I/O and reads no clock.
138
+
139
+ The event of a handler has the Gate as its [Sender](../reference/glossary.md#sender), with the Hook as its
140
+ [Caller](../reference/glossary.md#caller). The run takes the event only when it takes it from this Gate now, as for
141
+ `jr2 send`.
142
+
143
+ ## The look
144
+
145
+ A delivery can come before the Gate opens, or it can be lost while the
146
+ [Orchestrator](../reference/glossary.md#orchestrator) is down. The look reads the fact, so these cases come to the same
147
+ read:
148
+
149
+ - The look reads once after the Gate opens, again after each restart of the Orchestrator, and every `every` while the
150
+ Gate is open. The default `every` is `"10m"`.
151
+ - `until` gives one event of `delivers` from the read's output, or `undefined` for "not yet".
152
+ - When the run takes the event, the look stops until the Gate opens again. The run's feed shows `looked`. A looked event
153
+ has the Gate as its Sender and no Caller.
154
+ - The `http` Effect of the look is an Effect, so the secret that it names is an entry of the `secrets` list, as
155
+ `github-token` is in the example's `jr2.config.ts`. Put its value in `.env` as `GITHUB_TOKEN`.
156
+
157
+ A state that goes back to its own Gate must change the `meta`, for example to a new `sha`. If not, the look reads the
158
+ same verdict again.
159
+
160
+ Give `look: "none"` to a Gate that hears a command, such as a `/retry` comment. A command is not a fact that a read can
161
+ find, so a lost delivery of a command stays lost.
162
+
163
+ ## The Hook
164
+
165
+ The Gate hears a delivery only when the [Instance](../reference/glossary.md#instance) declares a Hook of its kind. Each
166
+ Hook is a name in the `hooks` block of `jr2.config.ts`, and its address is `POST /hooks/<name>`. Set up the service with
167
+ this address one time:
168
+
169
+ ```ts
170
+ import { defineConfig, secret } from "@jr2/orchestrator";
171
+ import { github } from "@jr2/orchestrator/hooks/github";
172
+ import { gitlab } from "@jr2/orchestrator/hooks/gitlab";
173
+
174
+ export default defineConfig({
175
+ // Each Hook's secret is an entry of the list. jr2 up places GH_HOOK in the Instance Effector, and
176
+ // gives the Orchestrator only the sha-256 of GL_HOOK.
177
+ secrets: [
178
+ { name: "GH_HOOK", value: process.env.GH_HOOK },
179
+ { name: "GL_HOOK", value: process.env.GL_HOOK },
180
+ // The look of review reads the reviews with this token. jr2 up places it in the Instance Effector.
181
+ { name: "github-token", value: process.env.GITHUB_TOKEN },
182
+ ],
183
+ hooks: {
184
+ // POST /hooks/forge: GitHub signs each delivery with the secret GH_HOOK. The Hook may answer
185
+ // the Gates of review alone.
186
+ forge: github({ secret: secret("GH_HOOK"), may: { answer: ["review"] } }),
187
+ // POST /hooks/lab: GitLab sends the secret GL_HOOK as a token in a header.
188
+ lab: gitlab({ secret: secret("GL_HOOK"), may: { answer: ["review"] } }),
189
+ },
190
+ });
191
+ ```
192
+
193
+ A Hook takes a credential and `may.answer`. `may.answer` is the list of Workflows whose Gates the Hook can answer. The
194
+ default is every Workflow. A Hook never starts a run. The kind tells how the service signs:
195
+
196
+ - `github` and `slack` sign with a shared secret (`mac`). The
197
+ [Instance Effector](../reference/glossary.md#instance-effector) holds the secret and checks the signature. The
198
+ Orchestrator does not hold the secret.
199
+ - `gitlab` sends a shared token in a header (`token`). The Orchestrator keeps only the sha-256 of the token, and removes
200
+ the header before the kind reads the delivery.
201
+ - `discord` signs with a key pair (`ed25519`). The config holds the public key, which cannot sign.
202
+
203
+ Each secret that a Hook names is an entry of the `secrets` list. For a `mac` Hook, [`jr2 up`](../reference/cli.md#up)
204
+ places the secret in the Instance Effector alone, as it places an
205
+ [Effect secret](../reference/glossary.md#effect-secret), and it converges the Instance Effector for the Hook. For a
206
+ `token` Hook, `jr2 up` writes the sha-256 of the secret into the Secret `jr2-hook-digests`, which the Orchestrator
207
+ mounts. The Orchestrator reads it again when it changes, so a new value needs no restart. The kubelet updates the mount
208
+ in about one minute, and `jr2 up` waits for it before it says `converged`. A kind that posts a follow-up, such as the
209
+ reply to a Slack click, declares the hosts of the post. `jr2 up` adds these hosts to the Instance Effector's list of
210
+ targets, with no secret. The output of `jr2 up` names the Hook as `hook forge`, in the line of the secret and in the
211
+ line of the Instance Effector.
212
+
213
+ When a delivery comes, the door does these steps in this sequence:
214
+
215
+ 1. The door refuses a body that is larger than 2 MiB. It reads no more of the body than this limit.
216
+ 2. The verifier checks the delivery. A signed time that is more than 300 seconds from the Orchestrator's clock fails the
217
+ check.
218
+ 3. The kind reads the delivery. A handshake (a GitHub `ping`, a Discord `PING`, a Slack `url_verification`) gets the
219
+ kind's answer and goes to no Gate.
220
+ 4. The kind's schema parses the payload.
221
+ 5. The door looks for a repeat: a delivery whose id or body this Hook got before.
222
+ 6. The delivery goes to each open Gate, in each live run of a Workflow that the Hook may answer, whose handlers hear its
223
+ kind and event key.
224
+
225
+ The answer is the outcome:
226
+ `{ delivery, seen, delivered: [{ run, workflow, gate, event }], refused: [{ run, gate, event?, reason }], ignored, sources }`.
227
+ `ignored` counts the open Gates whose handler gave `undefined`. A Gate whose handler throws, or gives an event that the
228
+ run does not take now, is in `refused`, and the run's feed shows `hook-refused`. The run continues.
229
+
230
+ To see what the door did, print the Hook's log. It keeps each delivery for `runs.retain` (30 days by default), newest
231
+ first:
232
+
233
+ ```sh
234
+ jr2 hooks forge
235
+ ```
236
+
237
+ ## A Gate in a package
238
+
239
+ A [packaged Machine](publish-a-machine-as-an-npm-package.md) writes its handlers, because a forge's payload is public.
240
+ It leaves its look `open`, because the look needs a url and a token. The composer binds the look with `customize()`, and
241
+ can add, replace or remove a handler by kind and event key, where `null` removes one:
242
+ `customize(machine, { gates: { review: { look, hooks: { gitlab: { "note.MergeRequest": null } } } } })`.
243
+
244
+ ## Rules
245
+
246
+ - The door refuses a delivery that fails the verifier, or whose signed time is more than 300 seconds from the
247
+ Orchestrator's clock, with 401. Nothing else occurs, and the Hook's log keeps no line.
248
+ - The door refuses a body that is larger than 2 MiB with 413, before the verifier:
249
+ `the delivery's body is larger than 2097152 bytes, the most the door reads` The Hook's log keeps a `too-large` line,
250
+ which does not claim the delivery's id.
251
+ - The door refuses a name that the config does not declare with 404:
252
+ `no Hook named "<name>" in the current Assembly's config`
253
+ - With no current Assembly, the door answers 503:
254
+ `the Orchestrator has no current Assembly, so no Hook is declared now — ask again`
255
+ - `jr2 up` refuses a `mac` Hook whose secret is not in the `secrets` list:
256
+ ``hooks.<name> names secret("<NAME>"), which the `secrets` list does not hold — add it: secrets: [{ name: "<NAME>", value: process.env.<NAME> }] (ADR-0068)``
257
+ It refuses a `token` Hook whose secret is not in the list with the same text, which ends with `(ADR-0078)`.
258
+ - `jr2 up` names the secret of a `token` Hook: `secret <NAME>: a digest in the Orchestrator (hook <name>)`
259
+ - A `mac` Hook whose check the Instance Effector cannot answer gets 503:
260
+ `the Hook's signature cannot be checked now: no Instance Effector (ADR-0068)`
261
+ - A request that the kind cannot read gets 400: `the <kind> kind cannot read the delivery: <error>`
262
+ - A payload that the kind's schema refuses gets 200, with `payloadRefused: { kind, key, issues }`. It goes to no Gate.
263
+ When the service sends it again, the door parses it again.
264
+ - A delivery whose id or body the Hook got before gets 200 with `seen: true`, and goes to no Gate.
265
+ - The config load refuses a Hook's name that is not lowercase letters, digits and `-`, at most 40 characters:
266
+ `<file> at hooks.<name>: a Hook's name is lowercase letters, digits and -, at most 40, starts with a letter and ends with a letter or digit (ADR-0078)`
267
+ - The config load refuses a value that no kind made:
268
+ `<file> at hooks.<name>: not a Hook — make it with its kind, such as github({ secret: secret("GH_HOOK") }) (ADR-0078)`
269
+ - `jr2 hooks <name>` needs the Instance token.
270
+
271
+ - `gate()` refuses a part that it does not know:
272
+ `gate(): "<key>" is not a part of a Gate slot (meta, delivers, hooks, look, reply) (ADR-0011)`
273
+ - `gate()` refuses an empty `delivers`: `gate(): its delivers lists at least one event that defineEvent made (ADR-0011)`
274
+ - `gate()` refuses a look whose read is a different Effect:
275
+ ``gate(): its look reads with <an http Effect that is not marked idempotent: true | a <kind> Effect> — a look reads again after each restore and every `every`, so its read is an http Effect marked idempotent: true, or a promise (ADR-0067) (ADR-0011)``
276
+ - `gate()` refuses an `every` that is not a duration:
277
+ `gate(): its look's every "<every>" is not a positive duration (like "90s", "10m", "2h") (ADR-0011)`
278
+ - `createMachine` refuses an event of `delivers` that the Machine does not declare:
279
+ `machine "<id>": Gate slot "<slot>" delivers "<event>", which no def in jr2Setup({ events }) declares (declared: <events>) — list it in events (ADR-0011)`
280
+ - `createMachine` refuses an event of `delivers` that the Gate does not take at the state that invokes it:
281
+ `machine "<id>": state "<state>" invokes Gate slot "<slot>", which delivers "<event>", and its Gate does not accept it (accepts: <events>) — no handler or look could deliver it: handle it on that state, a state above it or a plain child, or drop it from delivers (ADR-0011)`
282
+ - The Gate does not open when its `meta` does not agree with the schema, and the invoke fails:
283
+ `gate "<gate>": its meta does not match the Gate slot's meta schema — <path>: <message>`
284
+ - `customize()` refuses a slot that holds no Gate slot:
285
+ `customize(): machine "<id>" carries no Gate slot "<slot>" — its Gate slots are: <slots> (ADR-0011). Only declared parts can be bound.`
286
+ - `customize()` refuses `open` as a look:
287
+ ``customize(): Gate slot "<slot>"'s look is bound once, to a look or "none" — not `open` again (ADR-0054)``
288
+ - The Chart door refuses a Chart that invokes a lent Gate slot whose `delivers` holds an event that is not in the
289
+ Chart's `events`:
290
+ ``<path>: the Gate slot "<slot>" delivers "<event>", which is not in the Chart's `events` (events: <events>); declare it in `events` and handle it on this state``
291
+ - `jr2 up` and `jr2 check` warn, and do not refuse, about a Gate slot that hears a Hook and has no look and no `after`
292
+ on its state:
293
+ ``warning: workflow "<workflow>": Gate slot "<slot>" on "<state>" hears a Hook and has no look and no `after` on its state — a delivery lost in an outage stops the run until a person answers the Gate: give it a look, or look: "none" for a Gate that hears a command (ADR-0011)``
294
+ - `jr2 up` and `jr2 check` warn about a look that a package left `open` and that nobody bound:
295
+ `warning: workflow "<workflow>": Gate slot "<slot>" on "<state>" leaves its look open — a delivery lost in an outage stops the run until a person answers the Gate: bind it with customize(machine, { gates: { <slot>: { look } } }), or give it look: "none" (ADR-0011, ADR-0054)`
296
+
297
+ ## Read next
298
+
299
+ - [Gates](../concepts/gates.md) and [Callers and Hooks](../concepts/callers-and-hooks.md)
300
+ - [Write a Hook kind](write-a-hook-kind.md), for a service that jr2 ships no kind for
301
+ - [Answer a Gate](answer-a-gate.md) and [Bind Open parts](bind-open-parts.md)
302
+ - [ADR-0011](../reference/decisions/0011-workflow-defined-events.md): a Gate hears a Hook, and looks before it waits
303
+ - [ADR-0078](../reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md): a
304
+ Hook is a Caller that can only answer a Gate or bring items to a Source