@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
@@ -63,12 +63,23 @@ export default defineConfig({
63
63
  ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)): the credentials the Agents use — an
64
64
  Anthropic key, read from `process.env`/`.env`, held by the Custodian and never by the Harness (ADR-0059) — and the
65
65
  ones an Effect names, which only its Effector holds. Where each goes is read, never declared.
66
+ - **`mcp` is the Instance's MCP servers**
67
+ ([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)): each
68
+ by a name that the author chooses, remote (`url`, an optional `secret`) or stdio (`command`, `args`), with a
69
+ `timeout`. An Agent and an `mcp` Effect name a server by that name.
70
+ - **`callers`, `hooks` and `cors`** are the Instance's reach for Callers
71
+ ([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)): the named Caller tokens
72
+ and the JWT issuers with their scopes, the Hooks with their credentials and the Hook kinds that the config imports,
73
+ and the origins a browser app may call from.
66
74
  - **`registry`** (deployment-varying, resolve from env): absent → images are `kind load`-ed; present → pushed
67
75
  (ADR-0019). **`kitRegistry`** (also env) re-homes the published Kit image refs for self-hosted, air-gapped, or
68
76
  mirror-only clusters ([ADR-0044](0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md)).
69
77
  - **The snapshot store is sqlite** on a PVC in the instance's namespace (zero setup); no other store is owed
70
- ([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-a-week.md)) — the single-table, single-writer snapshot fits
71
- sqlite, and `replicas: 1` keeps it single-writer.
78
+ ([ADR-0065](0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md)) — the single-table, single-writer
79
+ snapshot fits sqlite, and `replicas: 1` keeps it single-writer. The kept conversations are a second sqlite file on the
80
+ same volume ([ADR-0023](0023-the-harness-prints-the-conversation.md)).
81
+ - **`runs`** sets how long jr2 keeps an ended run and the size of the state volume, `{ retain: "30d", volume: "5Gi" }`
82
+ by default (ADR-0065).
72
83
  - Git credentials for private repos: an HTTPS token from `.env`, or a `jr2-git-ssh` Secret holding the key `jr2 up`
73
84
  asked the user to choose — a generated in-cluster deploy keypair (the recommended default), a local key, or one pasted
74
85
  on stdin (ADR-0019, [ADR-0047](0047-the-git-ssh-key-source-is-the-users-choice.md)). Kube target: the current
@@ -79,28 +90,42 @@ export default defineConfig({
79
90
  The kit publishes to npm under `@jr2/*`; stock Agent definitions ship in **`@jr2/agents`** (coder, reviewer, …), and a
80
91
  package may ship whole Machines with their Agents and image inside (ADR-0049). Because a definition is plain data
81
92
  (ADR-0018), composition needs no API: a Machine carries `agent(coder)`, a workflows file exports a packaged Machine
82
- as-is or through `customize(machine, { agents: { coder: { model: "…" } } })`. Adding tools/skills waits on the
83
- definition contract growing that seat (ADR-0018).
93
+ as-is or through `customize(machine, { agents: { coder: { model: "…" } } })`. An Agent's tools beyond its own come from
94
+ the MCP servers that it names, and the same `customize()` rebinds them
95
+ ([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)). Adding
96
+ skills waits on the definition contract growing that seat (ADR-0018).
84
97
 
85
98
  ## Orchestrator HTTP API (hono, run-addressed-by-id)
86
99
 
87
100
  Push + control + observe only — the **pull** path needs no HTTP (the Orchestrator's Source pulls, ADR-0017). Auth bands
88
101
  per ADR-0014: structure + observation open; the Agent surface takes the Sandbox token; run state, control, and gates
89
- take the Instance token.
102
+ take a Caller with the scope in brackets (ADR-0078); `[instance]` is the Instance token alone.
90
103
 
91
104
  ```
92
105
  GET /workflows # list registered workflows [open]
93
106
  GET /workflows/:name/machine /viz/* # machine structure + visualizer [open]
94
107
  GET /workflows/:name/runs[, /:runId/events] # observation projection (ADR-0014) [open]
108
+ GET /workflows/:name/runs/:runId/machine # the run's own Machine, Charts compiled (ADR-0069) [open]
95
109
  GET /workflows/:name/events # SSE: the whole workflow, level-triggered (ADR-0022) [open]
96
- POST /workflows/:name/runs # start a run (push work); body = input → { runId } [instance]
97
- GET /runs GET /runs/:runId # live list; durable run status (read-through) [instance]
98
- GET /runs/resolve?prefix=<p> # run ids sharing a prefix, live + settled [instance]
99
- GET /runs/:runId/events # SSE: status replay + live deltas [instance]
100
- POST /runs/:runId/events # run-level infra interrupt: CANCEL [instance]
101
- POST /runs/:runId/gates/:gate/events # deliver a workflow event to an open gate [instance]
110
+ POST /workflows/:name/runs # start a run (push work); body = input → { runId } [start]
111
+ # a standing Workflow: resume a paused one [?drop-held=true] (ADR-0017)
112
+ GET /runs # run summaries; ?status ?workflow ?since ?cursor [read]
113
+ GET /runs/:runId # durable run status (read-through) [read]
114
+ GET /runs/resolve?prefix=<p> # run ids sharing a prefix, live + settled [any scope]
115
+ GET /runs/:runId/events # SSE: status replay + live deltas [read]
116
+ GET /runs/:runId/conversations # the run's kept conversations (ADR-0023) [read]
117
+ GET /runs/:runId/conversations/:iid[?after=<seq>] /events # entries; SSE follow [read]
118
+ GET /gates # open Gates the Caller may answer; ?workflow [answer]
119
+ POST /runs/:runId/events # run-level infra interrupt: CANCEL [cancel]
120
+ POST /runs/:runId/gates/:gate/events # deliver a workflow event to an open gate [answer]
121
+ POST /runs/:runId/steering # a Steering message to an Agent slot (ADR-0002) [steer]
122
+ GET /runs/:runId/agents # the slot list, one row per Agent slot [steer]
123
+ GET /whoami # the request's Caller record (ADR-0078) [any Caller]
124
+ POST /hooks/:name # a Hook's delivery (ADR-0011) [the Hook's verifier]
125
+ GET /hooks/:name # the Hook's log, newest first (ADR-0011) [instance]
102
126
  GET /repos # per-Repo node-cache sync state (ADR-0048) [instance]
103
127
  GET /agents/:iid/surface POST /agents/:iid/events # the Menu, via the Custodian [sandbox]
128
+ POST /agents/:iid/conversation # a batch of the record's entries (ADR-0023) [sandbox]
104
129
  POST /sandboxes/:name/fetch # the ask (ADR-0053) [sandbox]
105
130
  GET /healthz GET /readyz
106
131
  ```
@@ -112,11 +137,11 @@ ADR-0047's "register the key, the cache agent retries" points at. Instance band,
112
137
  carries git's error text, exactly the class of author/deployment detail the open projections strip (ADR-0014).
113
138
 
114
139
  Gates are the human/webhook/CI seam (ADR-0011): a gated state registers `{ gate, accepts, meta }`; `GET /runs/:runId`
115
- lists the open gates (with schemas + `meta`), and the gate POST validates the body against the named event schema and
116
- delivers it into that state. Per-gate addressing exists because concurrent children park concurrently — a run-level
117
- events POST is ambiguous. `CANCEL` is the one reserved run-level event; everything else is workflow vocabulary. It
118
- **ends** the run — its Agents' turns end with it and it does not restore
119
- ([ADR-0025](0025-cancel-ends-a-run-stop-parks-it.md)).
140
+ lists the open gates (with schemas + `meta`), `GET /gates` lists the open Gates of every live run that the Caller may
141
+ answer, and the gate POST validates the body against the named event schema and delivers it into that state. Per-gate
142
+ addressing exists because concurrent children park concurrently — a run-level events POST is ambiguous. `CANCEL` is the
143
+ one reserved run-level event; everything else is workflow vocabulary. It **ends** the run — its Agents' turns end with
144
+ it and it does not restore ([ADR-0025](0025-cancel-ends-a-run-stop-parks-it.md)).
120
145
 
121
146
  ## The `jr2` CLI
122
147
 
@@ -137,25 +162,78 @@ jr2 version [--local] [--json] # this jr2, the global that handed off, the I
137
162
 
138
163
  # runs / workflows (wrap the HTTP API)
139
164
  jr2 run <workflow> [--input <json>] [--detach]
140
- jr2 runs jr2 status [runId|abbrev] jr2 logs <runId|abbrev> [-f] # bare `status` = the instance's repos (ADR-0048)
165
+ jr2 run <workflow> [--drop-held] # resume a paused standing Pool (ADR-0017)
166
+ jr2 runs [--status <s>] [--workflow <w>] [--since <d>] # live runs by default; `--status ended|all|<list>`
167
+ jr2 workflows [--json] # the Workflows a run can start, with their origins and Charts (ADR-0070)
168
+ jr2 status [runId|abbrev] # bare `status` = the instance's repos (ADR-0048)
169
+ jr2 watch <runId|abbrev> [--json] # the run's status feed, followed to its end
170
+ jr2 logs <runId|abbrev> [agent] [-f] # the run's conversations, printed as ADR-0023 prints them
171
+ jr2 gates [--workflow <w>] # the open Gates this Caller may answer, oldest first
141
172
  jr2 send <runId|abbrev> --event CANCEL
142
173
  jr2 send <runId|abbrev> --gate <gate> --event <name> [--input <json>]
143
-
144
- # workspaces (kubectl-style, over the operator's Sandbox CRs) — decided, not yet in the binary
145
- jr2 ls # list workspaces + run + status + endpoint
146
- jr2 ssh <workspace> # exec into the Sandbox's harness container (ADR-0037)
147
- jr2 logs <workspace> jr2 rm <workspace>
174
+ jr2 steer <runId|abbrev> [<agent> "<text>"] # a Steering message; the slots alone (ADR-0002)
175
+
176
+ # Callers and Hooks (ADR-0078, ADR-0011)
177
+ jr2 whoami # the Caller this credential is
178
+ jr2 caller token <name> # print a named Caller token's value
179
+ jr2 caller rotate <name> # make a new value; the old one stops working
180
+ jr2 hooks <name> # the Hook's log of deliveries
181
+
182
+ # workspaces (kubectl-style, over the operator's Sandbox CRs; kube credentials only)
183
+ jr2 workspace ls [run] # the Workspaces, with run, path, status and age
184
+ jr2 workspace shell <ws> [--user] [-- <cmd>] # a shell in the Workspace, as its Agents work (--user: the User Container)
185
+ jr2 workspace forward <ws> <port> [--stdio] # a local port to a port in the pod (--stdio: an ssh ProxyCommand)
186
+ jr2 workspace rm <ws> # remove a Sandbox that no live run holds
187
+
188
+ # shell completion
189
+ jr2 completion bash|zsh|fish # the completion script, generated from the command table
148
190
  ```
149
191
 
150
- `jr2 ssh` is `kubectl exec -c harness` into the Sandbox Image
151
- ([ADR-0037](0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md)) — the inspect seat: the agent's own
152
- tools, worktrees, and filesystem. The optional User Container ([ADR-0005](0005-sandbox-pod-composition.md)) is reached
153
- by its own front door (its sshd, or `kubectl exec -c user`), not by this verb — its point is sessions and services the
154
- agent's container must not host.
192
+ **The workspace verbs read the cluster, not the Orchestrator.** They use the kube credentials of the current context and
193
+ no Caller scope, so they work when the Orchestrator is down, and kube RBAC is the gate. A Workspace's handle is
194
+ `<run-prefix>/<workspace path>`: a prefix of its run's id and its path in the run's Machine, read off the CR's label
195
+ `jr2.dev/run` and its annotation `jr2.dev/workspace-path` ([ADR-0012](0012-workspace-wrapper-machine.md)). The CLI lists
196
+ the instance's CRs and compares on the client, because a label selector cannot match a prefix. `workspace ls` prints the
197
+ exact path. The CR's name is also a handle, and a handle that ends in `.jr2` is the CR's name with that suffix, for the
198
+ ssh config below. A handle that matches more than one Workspace prints the candidates and fails, as a run prefix does.
199
+
200
+ - **`workspace shell`** asks the Harness to start a shell through its spawner, so the shell is a process of the Agents'
201
+ Landlock domain, with its env, the Workspace's grants and its cwd
202
+ ([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)). It can signal and trace
203
+ the Agents' processes, as a later `bash` call can. So the shell has the agent's own tools, worktrees and filesystem
204
+ ([ADR-0037](0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md)), and it never reaches the control
205
+ Stand-in or any Stand-in that the Workspace does not grant. `-- <cmd>` runs one command in place of a shell. `--user`
206
+ is `kubectl exec -c user` into the optional User Container ([ADR-0005](0005-sandbox-pod-composition.md)), where jr2
207
+ starts nothing of its own.
208
+ - **`workspace forward`** is a port-forward to the Sandbox pod: a local port to `<port>` in the pod's network namespace,
209
+ which reaches a server the Agent started or the User Container's sshd. It prints `127.0.0.1:<local port>` and holds
210
+ the forward until Ctrl-C. With `--stdio` it carries the connection on its stdin and stdout, so it is an ssh
211
+ `ProxyCommand`: `Host *.jr2` with `ProxyCommand jr2 workspace forward %h 22 --stdio -n <namespace>`, where the host is
212
+ the CR's name with `.jr2` after it. ssh runs the command in any folder, and with `-n` the verb needs no instance
213
+ folder. There is no routable Service for the User Container; the forward reaches it through the kube API.
214
+ - **`workspace rm`** deletes a Sandbox that no live run holds: an ended run's Sandbox that waits for its idle timeout,
215
+ or one that a fault left as evidence (ADR-0021). It refuses the Sandbox of a live run and says "cancel the run first".
216
+ It tells from the cluster alone, with no Orchestrator, by the hold mark that the Lease writes on the CR (ADR-0021):
217
+ `jr2.dev/hold` is `held` from the Lease's first write, and the Lease sets it to `released` when it stops for the
218
+ body's final state, a fault, a cancel or a parent that leaves the Workspace's state, but not while a live Workspace of
219
+ the run holds the same name (a parent that invokes the same Machine again after a defect). A restore that ends a run
220
+ `drifted` or `lost` without hosting it marks each of its CRs `released`. A park and a process shutdown write nothing,
221
+ so the CR of a run that an Orchestrator crash left stays `held`. `rm` removes a CR that is `released`, and a CR whose
222
+ `jr2.dev/keepalive` is older than the CR's idle timeout, which the operator reaps then in any case. It refuses every
223
+ other CR, a CR with no mark included. It measures the keepalive's age on the API server's clock (the `Date` header of
224
+ its answer), not on the local clock, so a fast laptop clock cannot make a live Sandbox look free. There is no
225
+ `--force`: the run owns its Workspace's life (ADR-0012).
226
+
227
+ Pod logs stay `kubectl logs`. The conversations of a Workspace's Agents are `jr2 logs <run> [agent]`, which reads the
228
+ Orchestrator's record and works after the Sandbox is gone (ADR-0023).
229
+
230
+ **`jr2 completion`** prints the script for bash, zsh or fish, made from the command table, so a verb or a flag cannot
231
+ complete that the CLI does not take. It completes dynamic values too: Workspace handles (from the CR labels and the
232
+ `jr2.dev/workspace-path` annotation), live run ids, Workflow names, and the Agent slots of a run. A lookup that takes
233
+ more than 2 s offers nothing, so a slow cluster never holds the shell.
155
234
 
156
235
  The CLI is the everyday surface; HTTP is the machine-to-machine one. The workspace verbs make the orchestrator feel like
157
- `kubectl` for agents: the run↔workspace link rides on the CR's labels (`jr2.dev/run`, `jr2.dev/workflow`), so `jr2 ls`
158
- can group without the orchestrator being reachable. CLI and hono app sit on one shared API client.
236
+ `kubectl` for agents. CLI and hono app sit on one shared API client.
159
237
 
160
238
  A planned `jr2 build` (build + push + render manifests, no apply — the pure-GitOps CI verb) is deferred; `jr2 up` in CI
161
239
  covers the interim (ADR-0019).
@@ -165,9 +243,10 @@ covers the interim (ADR-0019).
165
243
  **Instance addressing.** The CLI finds its instance by walking up from cwd to the directory containing `jr2.config.ts` —
166
244
  the root marker. The _deployment_ is addressed by the current kube context + the instance's namespace: run-verbs
167
245
  port-forward the Orchestrator Service for the duration of the command and read the Instance token from its in-cluster
168
- Secret (kube RBAC is the gate). `--url` / `JR2_URL` (+ token env) overrides both — the ingress-exposed/remote-caller
169
- case — and skips the folder walk entirely. Every run-verb prints the context it targets on stderr, so ambient-context
170
- drift is visible (ADR-0019).
246
+ Secret (kube RBAC is the gate); its Caller is `token/instance`, on behalf of the kube user (ADR-0078). `--url` /
247
+ `JR2_URL` (+ `JR2_TOKEN`, any Caller's credential) overrides both — the ingress-exposed/remote-caller case — and skips
248
+ the folder walk entirely. Every run-verb prints the context it targets on stderr, so ambient-context drift is visible
249
+ (ADR-0019).
171
250
 
172
251
  **The command table.** Each verb is an entry in `packages/cli/src/command-table.ts`: its arguments, its flags, the env
173
252
  vars it reads, what it prints, and its ADRs, as data. The verb's `parseArgs` options come from its entry, and so does
@@ -178,12 +257,18 @@ usage line. The CLI Reference of the docs is generated from the same table
178
257
 
179
258
  **Two output classes (amended 2026-09-20).** The stream rule below — JSON result on stdout, activity on stderr — was
180
259
  written for `jr2 run`, whose reason is the pipe: `jr2 run … | jq` must yield the result alone. It generalises to every
181
- **result verb** — `run`, `runs`, `status <runId>`, `logs`, `kit push` — because their result is small and the stderr
182
- lines beside it are advisory. It does not generalise to a **report verb** — `version`, and `status` with no run id —
183
- whose output is a diagnosis a human reads whole: dual-printing there shows a JSON blob and a partial table, and the
184
- readable form exists nowhere. So a report verb prints its human table on **stdout**, and `--json` swaps the table for
185
- the one object. No `isatty` sniffing: output that changes with how it is invoked is a debugging trap of its own. `init`,
186
- `check`, `up`, `down`, `gc` and `send` are neither class — activity only, no result.
260
+ **result verb** — `run`, `runs`, `status <runId>`, `gates`, `kit push` — because their result is small and the stderr
261
+ lines beside it are advisory. It does not generalise to a **report verb** — `version`, `status` with no run id, `watch`,
262
+ `logs` and `workspace ls` — whose output a human reads whole: dual-printing there shows a JSON blob and a partial table,
263
+ and the readable form exists nowhere. So a report verb prints its human table or lines on **stdout**, and `--json` swaps
264
+ them for the JSON (for `watch`, the feed's JSON frames; `logs` has no `--json`). No `isatty` sniffing: output that
265
+ changes with how it is invoked is a debugging trap of its own. `init`, `check`, `up`, `down`, `gc`, `send`,
266
+ `caller rotate` and `workspace rm` are neither class — activity only, no result. `workspace shell` and
267
+ `workspace forward --stdio` give the terminal to the session or the connection. `whoami` is a result verb. A **value
268
+ verb** — `caller token`, `completion`, `workspace forward` (its local address) — prints one value alone on stdout, not
269
+ JSON, and activity on stderr, because its one use is `JR2_TOKEN=$(jr2 caller token ci)`, and a JSON string there needs
270
+ `jq -r` for nothing. `caller rotate` prints no value: the new one is a `caller token` away, and a value is printed only
271
+ when it is asked for.
187
272
 
188
273
  **`jr2 version` (amended 2026-09-20).** The report you paste into a bug: what is _here_ and what is _deployed_, side by
189
274
  side, so the gap between the halves is the answer ("you edited the pin and never ran `jr2 up`"). The local half: `cli`
@@ -225,13 +310,28 @@ orchestrator**, not a temporary per-invocation runtime — a jr2 run is durable
225
310
  outliving the CLI call. `--detach` starts the run, prints its `runId`, returns.
226
311
 
227
312
  **Attach / detach / re-attach.** A run lives server-side, so attaching = opening `GET /runs/:runId/events` and detaching
228
- = closing it (Ctrl-C); neither affects the run. `jr2 logs <runId> -f` re-attaches to any running run. On attach, the SSE
229
- **replays the run's current status immediately** (so a parked run shows where it is) before streaming live deltas; it
230
- carries `status` events (auto, per transition) and `emit` events (the workflow author's `emit({…})`).
313
+ = closing it (Ctrl-C); neither affects the run. `jr2 watch <runId>` re-attaches to any run: it prints the lines that
314
+ `jr2 run` prints while it is attached, follows the run to its end, and for an ended run prints its last status once. It
315
+ exits 0 when it followed the feed to its end, for a failed or a parked run too: its exit code is the watch's, and
316
+ `jr2 run`'s is the run's result. On attach, the SSE **replays the run's current status immediately** (so a parked run
317
+ shows where it is) before streaming live deltas; it carries `status` events (auto, per transition) and `emit` events
318
+ (the workflow author's `emit({…})`).
231
319
 
232
320
  **Terminal runs stay readable.** The final snapshot persists to the store before the run drops from the live registry,
233
321
  so `status` / `GET /runs/:runId` **read through to the store** — a completed run reports its terminal status/context
234
- instead of `404`. (`GET /runs` stays live-only; history via `?all` is deferred.)
322
+ instead of `404`.
323
+
324
+ **`GET /runs` lists history.** Its filters are `status` (`live` by default; `ended`, `all`, or a list of statuses),
325
+ `workflow` and `since` (a duration back from now, such as `7d`, or an ISO-8601 time). It lists the newest start first, a
326
+ page at a time: `limit` (50 by default, at most 500) and `cursor`, and the answer is `{ runs, next? }`. An item is a
327
+ summary, not the whole `RunStatus`:
328
+ `{ runId, workflow, status, reason?, startedBy?, startedAt, endedAt?, assembly?, openGates, placing?, dispatchedBy? }`,
329
+ where `placing` names the child paths of the Workspaces that wait for capacity
330
+ ([ADR-0064](0064-a-workspace-waits-for-capacity.md)) and `dispatchedBy` names the run that started this one
331
+ ([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)), because `jr2 runs` prints both. A `drifted` run is
332
+ listed, with its reason (ADR-0030). A `live` row whose restore a boot deferred or that failed is listed as `active`,
333
+ with the reason `waiting for restore`. A run is listed while its row is kept (`runs.retain`, ADR-0065). `jr2 runs` takes
334
+ the same filters as `--status`, `--workflow` and `--since`.
235
335
 
236
336
  **Run ids abbreviate to a unique prefix.** A run id is a bare uuid, so every id-taking verb takes a git-style short
237
337
  form: `jr2 status 1a2b3c4d`. **Prefix only** — not fuzzy, not suffix — which keeps it an indexed range scan and keeps
@@ -250,10 +350,10 @@ returns silently for an unknown run, so an unresolved CANCEL used to report succ
250
350
  The candidate set is the ids that **exist**, not the ones that read: `lost` runs are included, because dropping them
251
351
  would let a prefix they share with a live run resolve silently to the live one. The set unions the live registry with
252
352
  the store — neither alone is complete, since `persist()` is microtask-scheduled (a just-started run is live before it is
253
- stored) and a settled run is stored but not live. Note the deliberate tension with `GET /runs`'s deferred `?all`: this
254
- route does reveal that settled run **ids** exist to an Instance-token holder. It answers ids only — no status, no
255
- context, no gates — to keep that widening as small as the feature allows, and it is Instance-only rather than open,
256
- since prefix probing on an open route would be a run-id enumeration oracle.
353
+ stored) and a settled run is stored but not live. It answers ids only — no status, no context, no gates — to a Caller
354
+ with any scope for their Workflow, where `GET /runs` needs `read`, and it is guarded rather than open, since prefix
355
+ probing on an open route would be a run-id enumeration oracle. Any scope opens it, for the Workflows of that scope,
356
+ because a Caller that may only cancel or steer also addresses a run by a prefix.
257
357
 
258
358
  **`jr2 init` (v1).** Scaffolds the minimum runnable instance: `jr2.config.ts` (root marker), `package.json` (deps on
259
359
  `@jr2/*` + xstate), one starter `workflows/<name>.ts`, `images/default/Dockerfile` (the Sandbox Image every Workspace
@@ -266,10 +366,11 @@ lockfile is what picks one ([ADR-0043](0043-the-kit-is-tested-as-installed-a-loc
266
366
 
267
367
  ## Consequences
268
368
 
269
- - The instance-facing packages (`@jr2/cli`, `@jr2/orchestrator`, `@jr2/agent-protocol`) publish to npm; `@jr2/harness`
270
- ships inside a Kit image, never via npm
369
+ - The instance-facing packages (`@jr2/cli`, `@jr2/orchestrator`, `@jr2/agent-protocol`, `@jr2/machines`) publish to npm;
370
+ `@jr2/harness` ships inside a Kit image, never via npm
271
371
  ([ADR-0043](0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md)). The CLI ships as the `jr2`
272
372
  bin (`npx jr2`).
273
- - Workspaces are a first-class CLI resource backed by the operator's `Sandbox` CRs, label-linked to their runs.
373
+ - Workspaces are a first-class CLI resource backed by the operator's `Sandbox` CRs, label-linked to their runs and their
374
+ paths, and reached with kube credentials alone.
274
375
  - Code is never loaded from a third party at run time (ADR-0008, ADR-0069): discovery is over the instance's own
275
376
  Assembly, which only `jr2 up` publishes. A run may bind a Machine's Open body with a Chart, which is data (ADR-0070).
@@ -81,14 +81,16 @@ Cucumber.js**, living in a top-level `./features/` workspace package (`@jr2/e2e`
81
81
  one is the tier watching the fix rather than merely enjoying it: the retry is absorbed by design, so without it a
82
82
  cluster drifting toward the 90s window would keep passing — slightly slower, silently — until the day it did not.
83
83
  - **An opt-in `@console` tier for the Console's UX** (added with
84
- [ADR-0032](0032-the-console-unlocks-with-the-instance-token.md)). The Console's risky behavior is interaction —
84
+ [ADR-0032](0032-the-console-unlocks-with-a-callers-credential.md)). The Console's risky behavior is interaction —
85
85
  token-mode switching, the frame-triggered gate inbox, selection vs. folding — which no reducer test sees and no CLI
86
86
  step drives. These scenarios hold a **Playwright page inside ordinary Cucumber steps** (Playwright the _library_, not
87
87
  the `@playwright/test` runner): the browser is a second driver of the same black box, reusing the per-scenario
88
88
  orchestrator fixture unchanged, and a scenario may drive both (the CLI starts a run; the browser sees it park). Tagged
89
89
  `@console` and **excluded from the default profile** exactly as `@kind` is — the everyday suite must not require a
90
- Chromium install — but they need no docker: a host-booted orchestrator serves the Console fine. Scope discipline: they
91
- assert what a user does and sees, never pixels or layout; what the page _believes_ stays in `console-store.test.ts`.
90
+ Chromium install — but they need no docker: a host-booted orchestrator serves the Console fine. The one exception is
91
+ the login, which needs an identity provider: its scenario runs Dex in a container and is also tagged `@dex`. The
92
+ `console` profile runs it; a host with no docker runs `--tags "@console and not @dex"`. Scope discipline: they assert
93
+ what a user does and sees, never pixels or layout; what the page _believes_ stays in `console-store.test.ts`.
92
94
  - **Harness conformance, in `@jr2/harness`, in the default gate**
93
95
  ([ADR-0027](0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md)). The three tiers above all reach the
94
96
  Harness through the **stub Harness**, which is a hand-written model of the _wire_ — it holds no conversation, so it