@jr2/orchestrator 0.3.0 → 0.5.1

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 (354) hide show
  1. package/README.md +20 -2
  2. package/bin/server.ts +7 -4
  3. package/console/canvas.ts +27 -14
  4. package/console/components/app.ts +12 -1
  5. package/console/components/fleet.ts +52 -6
  6. package/console/components/machine-pane.ts +128 -2
  7. package/console/components/nav.ts +1 -1
  8. package/console/components/schema-form.ts +12 -4
  9. package/console/json-field.ts +23 -0
  10. package/console/main.ts +122 -39
  11. package/console/store.ts +321 -21
  12. package/console/style.css +111 -0
  13. package/docs/agents-block.md +5 -0
  14. package/docs/concepts/agents-and-turns.md +442 -0
  15. package/docs/concepts/architecture.md +230 -0
  16. package/docs/concepts/charts.md +143 -0
  17. package/docs/concepts/composition.md +357 -0
  18. package/docs/concepts/converge.md +254 -0
  19. package/docs/concepts/durable-runs.md +189 -0
  20. package/docs/concepts/effects.md +603 -0
  21. package/docs/concepts/gates.md +208 -0
  22. package/docs/concepts/machines-and-workflows.md +289 -0
  23. package/docs/concepts/pools-and-sources.md +230 -0
  24. package/docs/concepts/secrets-and-trust.md +222 -0
  25. package/docs/concepts/the-menu.md +378 -0
  26. package/docs/concepts/workspaces.md +678 -0
  27. package/docs/get-started/a-model-decides.md +563 -0
  28. package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
  29. package/docs/get-started/connect-a-model.md +248 -0
  30. package/docs/get-started/create-an-instance.md +194 -0
  31. package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
  32. package/docs/get-started/install.md +126 -0
  33. package/docs/guides/add-tools-to-the-sandbox.md +295 -0
  34. package/docs/guides/answer-a-gate.md +304 -0
  35. package/docs/guides/bind-open-parts.md +223 -0
  36. package/docs/guides/cancel-a-run.md +146 -0
  37. package/docs/guides/clean-up.md +206 -0
  38. package/docs/guides/fix-a-failed-converge.md +361 -0
  39. package/docs/guides/give-the-cluster-git-credentials.md +222 -0
  40. package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
  41. package/docs/guides/let-an-agent-write-a-chart.md +226 -0
  42. package/docs/guides/look-inside-a-sandbox.md +174 -0
  43. package/docs/guides/mirror-the-kit-images.md +176 -0
  44. package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
  45. package/docs/guides/open-the-console.md +91 -0
  46. package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
  47. package/docs/guides/push-and-sign-a-branch.md +486 -0
  48. package/docs/guides/run-a-chart-by-name.md +270 -0
  49. package/docs/guides/run-a-command-or-call-a-url.md +544 -0
  50. package/docs/guides/run-one-worker-per-item.md +321 -0
  51. package/docs/guides/set-an-agents-access-and-dials.md +375 -0
  52. package/docs/guides/size-a-workspace.md +204 -0
  53. package/docs/guides/start-a-run-from-a-run.md +237 -0
  54. package/docs/guides/start-a-run.md +207 -0
  55. package/docs/guides/upgrade-the-kit-version.md +240 -0
  56. package/docs/guides/use-a-hosted-provider.md +208 -0
  57. package/docs/guides/use-a-local-model.md +313 -0
  58. package/docs/guides/watch-a-run.md +326 -0
  59. package/docs/guides/write-a-workflow.md +318 -0
  60. package/docs/index.md +44 -0
  61. package/docs/llms.txt +216 -0
  62. package/docs/reference/authoring-surface/agent.md +128 -0
  63. package/docs/reference/authoring-surface/answer.md +140 -0
  64. package/docs/reference/authoring-surface/arg.md +60 -0
  65. package/docs/reference/authoring-surface/basic.md +62 -0
  66. package/docs/reference/authoring-surface/bearer.md +53 -0
  67. package/docs/reference/authoring-surface/chartschema.md +23 -0
  68. package/docs/reference/authoring-surface/chartschemafor.md +48 -0
  69. package/docs/reference/authoring-surface/command.md +191 -0
  70. package/docs/reference/authoring-surface/customize.md +64 -0
  71. package/docs/reference/authoring-surface/defineconfig.md +41 -0
  72. package/docs/reference/authoring-surface/defineevent.md +114 -0
  73. package/docs/reference/authoring-surface/dispatch.md +126 -0
  74. package/docs/reference/authoring-surface/fail.md +83 -0
  75. package/docs/reference/authoring-surface/file.md +122 -0
  76. package/docs/reference/authoring-surface/frompick.md +277 -0
  77. package/docs/reference/authoring-surface/http.md +159 -0
  78. package/docs/reference/authoring-surface/isfailure.md +58 -0
  79. package/docs/reference/authoring-surface/jr2setup.md +237 -0
  80. package/docs/reference/authoring-surface/open.md +74 -0
  81. package/docs/reference/authoring-surface/openbody.md +86 -0
  82. package/docs/reference/authoring-surface/paramsof.md +125 -0
  83. package/docs/reference/authoring-surface/pool.md +186 -0
  84. package/docs/reference/authoring-surface/push.md +112 -0
  85. package/docs/reference/authoring-surface/secret.md +118 -0
  86. package/docs/reference/authoring-surface/sentby.md +83 -0
  87. package/docs/reference/authoring-surface/source.md +51 -0
  88. package/docs/reference/authoring-surface/text.md +62 -0
  89. package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
  90. package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
  91. package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
  92. package/docs/reference/authoring-surface/type-agentslots.md +12 -0
  93. package/docs/reference/authoring-surface/type-answeraction.md +12 -0
  94. package/docs/reference/authoring-surface/type-arg.md +19 -0
  95. package/docs/reference/authoring-surface/type-argnames.md +12 -0
  96. package/docs/reference/authoring-surface/type-binding.md +62 -0
  97. package/docs/reference/authoring-surface/type-bindingof.md +12 -0
  98. package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
  99. package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
  100. package/docs/reference/authoring-surface/type-ceilings.md +41 -0
  101. package/docs/reference/authoring-surface/type-chainlink.md +19 -0
  102. package/docs/reference/authoring-surface/type-chartaction.md +46 -0
  103. package/docs/reference/authoring-surface/type-chartagent.md +28 -0
  104. package/docs/reference/authoring-surface/type-chartevent.md +20 -0
  105. package/docs/reference/authoring-surface/type-chartguard.md +20 -0
  106. package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
  107. package/docs/reference/authoring-surface/type-chartjson.md +14 -0
  108. package/docs/reference/authoring-surface/type-chartstate.md +46 -0
  109. package/docs/reference/authoring-surface/type-charttext.md +40 -0
  110. package/docs/reference/authoring-surface/type-charttransition.md +22 -0
  111. package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
  112. package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
  113. package/docs/reference/authoring-surface/type-childat.md +12 -0
  114. package/docs/reference/authoring-surface/type-childslots.md +12 -0
  115. package/docs/reference/authoring-surface/type-commandenv.md +15 -0
  116. package/docs/reference/authoring-surface/type-commandinput.md +19 -0
  117. package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
  118. package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
  119. package/docs/reference/authoring-surface/type-customize.md +109 -0
  120. package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
  121. package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
  122. package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
  123. package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
  124. package/docs/reference/authoring-surface/type-duration.md +15 -0
  125. package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
  126. package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
  127. package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
  128. package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
  129. package/docs/reference/authoring-surface/type-eventdef.md +45 -0
  130. package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
  131. package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
  132. package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
  133. package/docs/reference/authoring-surface/type-failslot.md +14 -0
  134. package/docs/reference/authoring-surface/type-failure.md +19 -0
  135. package/docs/reference/authoring-surface/type-failureinput.md +20 -0
  136. package/docs/reference/authoring-surface/type-filebody.md +41 -0
  137. package/docs/reference/authoring-surface/type-frominput.md +14 -0
  138. package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
  139. package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
  140. package/docs/reference/authoring-surface/type-httpinput.md +12 -0
  141. package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
  142. package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
  143. package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
  144. package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
  145. package/docs/reference/authoring-surface/type-jr2config.md +126 -0
  146. package/docs/reference/authoring-surface/type-lends.md +38 -0
  147. package/docs/reference/authoring-surface/type-logicat.md +12 -0
  148. package/docs/reference/authoring-surface/type-markedif.md +12 -0
  149. package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
  150. package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
  151. package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
  152. package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
  153. package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
  154. package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
  155. package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
  156. package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
  157. package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
  158. package/docs/reference/authoring-surface/type-openeffect.md +12 -0
  159. package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
  160. package/docs/reference/authoring-surface/type-openof.md +12 -0
  161. package/docs/reference/authoring-surface/type-opensecret.md +23 -0
  162. package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
  163. package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
  164. package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
  165. package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
  166. package/docs/reference/authoring-surface/type-poolspec.md +60 -0
  167. package/docs/reference/authoring-surface/type-pushinput.md +15 -0
  168. package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
  169. package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
  170. package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
  171. package/docs/reference/authoring-surface/type-quantity.md +14 -0
  172. package/docs/reference/authoring-surface/type-reached.md +12 -0
  173. package/docs/reference/authoring-surface/type-reposlot.md +70 -0
  174. package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
  175. package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
  176. package/docs/reference/authoring-surface/type-secret.md +25 -0
  177. package/docs/reference/authoring-surface/type-sender.md +19 -0
  178. package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
  179. package/docs/reference/authoring-surface/type-size.md +85 -0
  180. package/docs/reference/authoring-surface/type-slothandles.md +32 -0
  181. package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
  182. package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
  183. package/docs/reference/authoring-surface/type-syncstate.md +12 -0
  184. package/docs/reference/authoring-surface/type-text.md +19 -0
  185. package/docs/reference/authoring-surface/type-textpart.md +24 -0
  186. package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
  187. package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
  188. package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
  189. package/docs/reference/authoring-surface/type-unknown.md +21 -0
  190. package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
  191. package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
  192. package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
  193. package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
  194. package/docs/reference/authoring-surface/type-workspaced.md +94 -0
  195. package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
  196. package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
  197. package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
  198. package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
  199. package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
  200. package/docs/reference/authoring-surface/workspace.md +259 -0
  201. package/docs/reference/authoring-surface.md +197 -0
  202. package/docs/reference/cli.md +433 -0
  203. package/docs/reference/config.md +756 -0
  204. package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
  205. package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
  206. package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
  207. package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
  208. package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
  209. package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
  210. package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
  211. package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
  212. package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
  213. package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
  214. package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
  215. package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
  216. package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
  217. package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
  218. package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
  219. package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
  220. package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
  221. package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
  222. package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
  223. package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
  224. package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
  225. package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
  226. package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
  227. package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
  228. package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
  229. package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
  230. package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
  231. package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
  232. package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
  233. package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
  234. package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
  235. package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
  236. package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
  237. package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
  238. package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
  239. package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
  240. package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
  241. package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
  242. package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
  243. package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
  244. package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
  245. package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
  246. package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
  247. package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
  248. package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
  249. package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
  250. package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
  251. package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
  252. package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
  253. package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
  254. package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
  255. package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
  256. package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
  257. package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
  258. package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
  259. package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
  260. package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
  261. package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
  262. package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
  263. package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
  264. package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
  265. package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
  266. package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
  267. package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +550 -0
  268. package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
  269. package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
  270. package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
  271. package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
  272. package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
  273. package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
  274. package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
  275. package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
  276. package/docs/reference/decisions.md +83 -0
  277. package/docs/reference/glossary.md +648 -0
  278. package/docs/reference/http-api.md +1348 -0
  279. package/docs/reference/machines.md +190 -0
  280. package/docs/reference/mechanism-events.md +72 -0
  281. package/docs/why.md +206 -0
  282. package/package.json +6 -4
  283. package/src/actor.ts +976 -61
  284. package/src/agent.ts +114 -38
  285. package/src/ambient.ts +4 -1
  286. package/src/assembly-checks.ts +347 -0
  287. package/src/assembly-tar.ts +494 -0
  288. package/src/assembly.ts +721 -0
  289. package/src/cause.ts +234 -0
  290. package/src/chart.ts +2182 -0
  291. package/src/charts.ts +748 -0
  292. package/src/config.ts +520 -162
  293. package/src/content-hash.ts +105 -0
  294. package/src/custodian.lua +6 -5
  295. package/src/custodian.ts +143 -39
  296. package/src/customize.ts +388 -55
  297. package/src/dispatch-effect.ts +945 -0
  298. package/src/door-compat.ts +217 -0
  299. package/src/durability.ts +11 -4
  300. package/src/effect-checks.ts +557 -0
  301. package/src/effect.ts +1202 -0
  302. package/src/effector-wire.ts +674 -0
  303. package/src/fail.ts +209 -0
  304. package/src/fingerprint.ts +4 -3
  305. package/src/gate.ts +8 -0
  306. package/src/git-clone.ts +65 -0
  307. package/src/git-push.ts +336 -0
  308. package/src/harness-client.ts +764 -48
  309. package/src/held-secrets.ts +82 -26
  310. package/src/holds.ts +156 -0
  311. package/src/http-effect.ts +1423 -0
  312. package/src/http.ts +916 -113
  313. package/src/images.ts +47 -21
  314. package/src/index.ts +62 -43
  315. package/src/instance.ts +505 -59
  316. package/src/internal.ts +84 -0
  317. package/src/kube-client.ts +333 -0
  318. package/src/machine-doc.ts +106 -6
  319. package/src/mechanism-routes.ts +168 -0
  320. package/src/model-keys.generated.ts +53 -0
  321. package/src/model-keys.ts +18 -58
  322. package/src/mount-digest.ts +53 -0
  323. package/src/names.ts +66 -8
  324. package/src/notices.ts +141 -0
  325. package/src/open-body.ts +1580 -0
  326. package/src/open-effect.ts +217 -0
  327. package/src/open.ts +59 -6
  328. package/src/parts.ts +709 -41
  329. package/src/party.ts +534 -0
  330. package/src/pick.ts +761 -0
  331. package/src/pool.ts +248 -37
  332. package/src/registration.ts +382 -19
  333. package/src/repo-fetch.ts +60 -85
  334. package/src/repos.ts +65 -75
  335. package/src/rest.ts +133 -0
  336. package/src/run-host.ts +2383 -115
  337. package/src/run-output.ts +38 -0
  338. package/src/sandbox-effect.ts +1126 -0
  339. package/src/sandbox-effector.ts +111 -0
  340. package/src/sandbox-kube.ts +1721 -0
  341. package/src/sandbox-watch.ts +260 -0
  342. package/src/secret-placement.ts +529 -0
  343. package/src/server.ts +284 -84
  344. package/src/setup.ts +819 -117
  345. package/src/size.ts +454 -0
  346. package/src/snapshot-store.ts +115 -20
  347. package/src/stub-harness.ts +49 -16
  348. package/src/sync-client.ts +54 -0
  349. package/src/timers.ts +191 -0
  350. package/src/tokens.ts +35 -4
  351. package/src/vocabulary.ts +135 -15
  352. package/src/wire.ts +238 -10
  353. package/src/workspace.ts +1195 -338
  354. package/src/sandbox-kubectl.ts +0 -1108
@@ -0,0 +1,180 @@
1
+ # The Orchestrator is a Kit image, and an Instance publishes its Assembly
2
+
3
+ [ADR-0008](0008-orchestrator-as-deployed-app.md) and
4
+ [ADR-0019](0019-one-converging-command-against-the-current-context.md) made the Orchestrator the one image an Instance
5
+ produces: the engine and the Instance's `workflows/` in one content-addressed image, rebuilt and rolled on every edit to
6
+ a Machine, to `jr2.config.ts`, or to a Sandbox Dockerfile under `images/`. The Harness and the operator had already
7
+ become Kit images released at one version ([ADR-0038](0038-jr2-up-builds-every-image-it-deploys.md),
8
+ [ADR-0044](0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md)), so the Orchestrator was the one
9
+ jr2-owned process whose image a user built. Three costs followed. Every Machine edit rolled the daemon that hosts every
10
+ run, which is the roll [ADR-0038](0038-jr2-up-builds-every-image-it-deploys.md) moved the image map to a ConfigMap to
11
+ avoid. An installed Instance needed docker for `jr2 up` even when it had no `workspace()` and so no image of its own to
12
+ build. And a Machine edit stranded every parked run of that Workflow at the deploy
13
+ ([ADR-0030](0030-a-snapshot-names-the-machine-it-was-written-under.md)), by design, because the new process could
14
+ resolve a Workflow's name only to the new Machine. The grill of 2026-10-18 split the one artifact into two.
15
+
16
+ ## Decision
17
+
18
+ - **The Orchestrator is a Kit image.** `ghcr.io/snapwich/jr2-orchestrator:<kitversion>`, built content-addressed in a
19
+ checkout and published at the version with the Harness and the operator, mirrored by `jr2 kit push`, re-homed by
20
+ `kitRegistry` ([ADR-0038](0038-jr2-up-builds-every-image-it-deploys.md),
21
+ [ADR-0044](0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md)). It holds `@jr2/orchestrator` and its
22
+ dependency closure and nothing of any Instance. The closure is installed hoisted at `/instance/node_modules`, and an
23
+ Assembly lives at `/instance/.jr2/assemblies/<hash>/`, so Node's walk up from any file in an Assembly reaches the
24
+ image's copies: that walk is the resolution rule of the next bullet. The third-party closure is pinned at the versions
25
+ the kit's own lockfile resolves (`deploy/orchestrator/closure.json`, checked against the lockfile by a test and by the
26
+ build), so one Kit tag names the same bytes on every day it is built, and the image runs the versions every test tier
27
+ runs. The image an Instance produced retires: an installed Instance with no `workspace()` needs no docker for
28
+ `jr2 up`.
29
+ - **An Instance publishes an Assembly.** The Assembly is the Instance's Workflows, its `jr2.config.ts`, the `file:`
30
+ image contexts its Machines name, and what they import, staged as one tree with the Kit's packages stripped (below),
31
+ sealed, and hashed. The tree is hoisted on both bundle paths of
32
+ [ADR-0043](0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md) (`pnpm deploy` too), so the
33
+ Kit's packages are top-level entries the strip deletes, and no pnpm store name reaches the archive. The hash covers
34
+ the tree's structure as well as its bytes: every path in byte order, each file's content, each symlink's target, each
35
+ directory including an empty one, and no mode, so one hash names one Assembly on any host in any locale. The archive
36
+ is a ustar tarball with pax `path`/`linkpath` records for the names ustar cannot hold, relative symlinks only, every
37
+ entry refused that would escape the tree. `jr2 up` publishes it to the running Orchestrator over the Orchestrator's
38
+ own API, and only `jr2 up` does: an Agent never publishes code
39
+ ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)). The config rides the Assembly because it is TypeScript too
40
+ and the Orchestrator reads it at boot; what was Deployment env stays env (`registry`, `kitRegistry`, the Secret refs).
41
+ Rejected: one bundled file per Workflow, which breaks `import.meta.resolve` for a context a Machine ships and splits
42
+ one lockfile into many; a config ConfigMap published beside the Assembly, which is a second artifact that can disagree
43
+ with the first.
44
+ - **A package the Kit image ships resolves from the image, never from the Assembly.** The publisher strips
45
+ `@jr2/orchestrator` and its whole dependency closure from the staged tree, and the Orchestrator resolves them to its
46
+ own copies. Class identity holds by construction, not by dedupe: a second copy of `@jr2/orchestrator`, `xstate` or
47
+ `zod` is a Machine whose Menus are empty with no error anywhere
48
+ ([ADR-0054](0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)). "Its closure", not a list of
49
+ three, so a new kit dependency cannot reopen the hole. The Assembly keeps `@jr2/machines` and the Instance's own deps.
50
+ - **An Assembly names its exact Kit version, and the door refuses any other.** The Assembly's manifest names the
51
+ `@jr2/orchestrator` version it was written against; admission refuses a mismatch naming both numbers, the refusal of
52
+ [ADR-0056](0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md) moved to the door.
53
+ Not semver: the kit is 0.x and its minors break, `@jr2/machines` peers exact for the same reason, and a compatibility
54
+ range is a promise jr2 cannot test without a version matrix. Loosening is one line later; tightening after a range has
55
+ been promised is not. So one `jr2 up` that bumps the Kit version rolls the Orchestrator image first and publishes the
56
+ Assembly built against the new version second. The new image boots on the old pointer, whose record names the old Kit
57
+ version, and refuses it as the door would: between the roll and the publish the Orchestrator serves and holds no
58
+ current Assembly: for a kubelet ConfigMap period in a converge, and until the next `jr2 up` after a volume loss. In
59
+ that window `/agents/*` answers 503 for a surface the process does not host, never 404
60
+ ([ADR-0026](0026-a-turn-that-is-over-is-never-prompted.md)). A boot with no current Assembly restores nothing, because
61
+ no config is bound in that window and a run that provisioned under none would fault for good; it leaves the live rows
62
+ untouched, announces them as `waiting`, and the first publish restores them, each inside the Assembly its snapshot
63
+ names. A run whose Assembly was published for the old Kit version is `drifted` at that publish, naming the hash and
64
+ both versions. That verdict is final, and the collection that follows removes the Assembly: an image rolled back
65
+ before the publish still finds every run live, an image rolled back after it does not.
66
+ - **The route is one noun and two verbs.** `PUT /assembly`, behind a **publish token**: one tarball, the Kit version and
67
+ the content hash in headers; `200 { hash, kitVersion, workflows, held, collected }` or `4xx` carrying the walk's
68
+ report, the old Assembly untouched. `held` lists every Assembly a live run holds, the current one first, each with the
69
+ runs that hold it; `collected` names the Assemblies the publish let go. The publish token is not the Instance token.
70
+ Publishing an Assembly is importing code into a process that holds the ServiceAccount, so it must cost what deploying
71
+ an image cost: cluster write access. The token lives in the Instance's Secret beside the Instance token, `jr2 up`
72
+ reads it through the kube API alone, and no env var and no `--url` path can supply it, so an ingress-exposed Instance
73
+ exposes no publish door. The Instance token keeps its meaning
74
+ ([ADR-0032](0032-the-console-unlocks-with-the-instance-token.md)): start runs, answer Gates, read. `GET /assembly`,
75
+ public like `GET /workflows`: the current hash and Kit version, the Assemblies held with the runs that hold them, and,
76
+ when deployed, the digest of each ConfigMap the process reads through a mount (`jr2-images`, `jr2-held`), so a
77
+ publisher can see what the process reads. `jr2 status` prints it; `/healthz` prints the current hash where it printed
78
+ the content hash. A repeat publish of the current hash answers `200 { unchanged: true }` before the body is read;
79
+ `--force` sends the PUT anyway and gets the same answer, because the hash names bytes the Orchestrator already holds.
80
+ Rejected: an OCI artifact the Orchestrator pulls, which needs the registry that dropping docker removed; a ConfigMap,
81
+ at 1 MiB; a git ref the Orchestrator clones, when [ADR-0048](0048-the-orchestrator-boots-without-its-repos.md) just
82
+ took cloning out of the Orchestrator.
83
+ - **The walk runs twice, with different duties.** On the host, `jr2 up` keeps the typecheck
84
+ ([ADR-0050](0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md)) and every duty that needs the
85
+ host or the cluster: it loads the Instance folder with the same `loadWorkflows` and `partsOf` the Orchestrator runs,
86
+ stages the Assembly before any cluster spend and refuses a converge whose staged Workflow set differs from the walked
87
+ one, builds and delivers `file:` Sandbox Images keyed by the digest of each context's staged copy (the bytes the door
88
+ and every provision hash; the folder's own copy may hold a `.env` or a `node_modules` that staging drops), writes the
89
+ images ConfigMap, materializes Secrets and held-secret Secrets, warms Repos, preflights the provider, and refuses
90
+ every Open part nobody bound in one listing, the listing the door produces too. Then it publishes, after the cluster
91
+ objects exist and after any image roll. At the door the Orchestrator unpacks to staging, refuses a Kit package carried
92
+ in the tree, imports the config and every `workflows/*.ts`, which is the first check (no `export const machine`, a
93
+ `jr2Setup.createMachine` refusal), and runs the same walk to refuse what it can see: a zod `input` that no door reads
94
+ (ADR-0033), an Open part, a secret the config does not declare, a `file:` digest absent from its map, a Vocabulary
95
+ error, a Kit version mismatch. The door checks are true when the Assembly lands because the cluster work came first,
96
+ and because the publish waits for the kubelet: a publish rolls no pod, and the ConfigMaps the converge wrote reach the
97
+ process on the kubelet's own period, so `jr2 up` compares the digests `GET /assembly` reports with the data it applied
98
+ and sends the PUT only once they agree. The server has no `tsc`: types are the author's duty and the typecheck is the
99
+ host's gate; a type error that survives shows as an import error at the door, named in the refusal.
100
+ - **A publish is atomic and reloads; it never rolls.** The Orchestrator unpacks to `assemblies/<hash>/` on its own
101
+ volume beside `state.db`, fsyncs, flips one `current` pointer, and reloads: the `reload()` the deployed entrypoint
102
+ never used. A crash before the flip leaves the old Assembly current; after it, the new. The pod rolls only when the
103
+ Kit version or its env changes; the Instance Secret reaches the process as env, so a pod-template digest of it rolls
104
+ the pod when a token is minted anew, and never otherwise. The entrypoint boots from the current Assembly and never
105
+ from the folder it starts in: a host-booted fixture publishes through the same door `jr2 up` does. Volume loss boots
106
+ an Orchestrator that serves, announces "no Assembly", and holds no Workflows until the next `jr2 up`, the pattern of
107
+ [ADR-0048](0048-the-orchestrator-boots-without-its-repos.md): degrade the capability, not the daemon.
108
+ - **A run holds the Assembly it started under.** A new run starts under the current Assembly. A live run keeps the one
109
+ it started under, through restarts too: the snapshot records the Assembly hash, and restore resolves the Workflow's
110
+ name inside that Assembly, imported once per Assembly and never registered, so `jr2 runs`, `jr2 run <name>` and
111
+ `GET /workflows/<name>/machine` are the current Assembly's alone. The Orchestrator holds the current Assembly and
112
+ every Assembly that a live run holds, and lets one go when no live run holds it, by reachability
113
+ ([ADR-0039](0039-image-garbage-collects-by-reachability.md)): at boot after restore, after each publish, and on the
114
+ hourly sweep; a hold is the live row, so a run this process failed to restore still holds its Assembly; the hash the
115
+ `current` pointer names is a root even when this process cannot load it, so an image rolled back finds its Assembly; a
116
+ failed removal is reported and skipped, never allowed to abandon the rest. A held Assembly lends only its Machines:
117
+ the config that applies is always the current Assembly's, because config is reach and credentials, not a Machine, and
118
+ a held run must meet today's `git.credentials` fence, not the one it started under. The Repo pass binds the Repos of
119
+ every held Machine, so a held run's Workspace mounts what its own Machine binds. The cluster parts `jr2 up` derives
120
+ from the current walk alone are the current Assembly's too: the `file:` Sandbox Image digests in `jr2-images`, the
121
+ Effector manifests, the held-secret Secrets, and the roots of the image sweep. A held run that still needs one the
122
+ current Assembly dropped fails at that provision, naming the held Assembly; so a publish strands no run whose Machine
123
+ it changed, and a publish that drops a Sandbox Image context a parked run has not yet provisioned is the one edit that
124
+ still can. The Console draws a run on the current Machine; a held run's `assembly` is in its status, and a Console
125
+ that marks or draws held runs is a later decision. So a publish strands nothing: the "editing a workflow strands its
126
+ parked runs" consequence of [ADR-0030](0030-a-snapshot-names-the-machine-it-was-written-under.md) is retired, and
127
+ drift is what it always meant, "I can no longer read what I saved": an Assembly that cannot load after a Kit version
128
+ bump, or a volume that lost it. Such a run is `drifted` with a reason naming the Assembly hash and the import error;
129
+ no new status. Drift keeps the snapshot, as it always did; `lost`, which drops it, is for a blob with nothing to keep:
130
+ an empty snapshot, one that names no Workflow, or a live world that `reconcile` reports gone
131
+ ([ADR-0007](0007-durable-machine-state.md)). A fault while reading a held Assembly (a permission, a record that does
132
+ not parse) is `failed`, not drift, and the row stays live and holding
133
+ ([ADR-0030](0030-a-snapshot-names-the-machine-it-was-written-under.md)). Rejected: report the runs a publish will
134
+ strand and let them drift at the next restart, which turns today's loud stranding into a deferred, silent one.
135
+ - **The data plane is always wired.** `serverMain` decided at boot, from the walk, whether to wire Sandbox support at
136
+ all; a reload can publish the first `workspace()`. The Orchestrator always wires its kube client, and a provision on a
137
+ cluster where `jr2 up` deployed no operator (`operator.manage: false`) fails pointedly naming `jr2 up`. `jr2 up` keeps
138
+ deciding what to DEPLOY for the walk's Sandboxes from its host-side walk, the Sandbox Images and the cache agent;
139
+ wiring a client is free, deploying those is not. The one Sandbox watch starts when the current Assembly composes a
140
+ Sandbox, or at the first provision, so a cluster with no Sandbox kind is not watched for one.
141
+ - **The seal simplifies.** [ADR-0038](0038-jr2-up-builds-every-image-it-deploys.md)'s rule stands: the staged tree
142
+ records nothing about where or when it was staged, so one hash names one Assembly. The `.bin` shims it rewrote are
143
+ deleted instead: the Orchestrator runs no bin from an Assembly (an Effect runs in an Effector,
144
+ [ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)), and a path that names the Assembly's own
145
+ directory cannot be rewritten to a path that contains its own hash.
146
+
147
+ ## Considered options
148
+
149
+ - **Keep the per-Instance image and roll less.** Rejected: nothing short of a separate artifact stops a Machine edit
150
+ from being a pod-template change, and the roll is what strands parked runs.
151
+ - **Let an Agent publish an Assembly through an Effect holding the Instance token, behind a Gate.** Rejected: the
152
+ Assembly is code the Orchestrator imports with its ServiceAccount and signing key in reach, so the only fence is who
153
+ may publish, and a reviewer approving generated code under time is a weak fence for a process that holds the cluster.
154
+ The agent path is data: [ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md).
155
+ - **Semver-compatible Assemblies.** Rejected above; one line to loosen later.
156
+ - **Keep only the current Assembly and report the runs a publish will strand.** Rejected above.
157
+
158
+ ## Consequences
159
+
160
+ - The deployable unit of [ADR-0008](0008-orchestrator-as-deployed-app.md) is rewritten: a Kit image plus a published
161
+ Assembly. Under GitOps the manifests alone no longer reproduce the Workflows; the Instance repo plus `jr2 up` does,
162
+ which was already the record and already the CI path.
163
+ - `npm i -g @jr2/cli && jr2 init && jr2 up` works with no docker on the host for an Instance with no `workspace()`,
164
+ which closes the promise of [ADR-0044](0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md) for the
165
+ common case. A Sandbox Dockerfile edit no longer rolls the Orchestrator.
166
+ - The content hash moves off the Deployment: the Deployment's image is the Kit's, and the Assembly hash is an
167
+ Orchestrator fact, read from `GET /assembly`, not a label `jr2 up` compares. The steady-state converge compares the
168
+ staged hash to the current one and publishes nothing when they agree.
169
+ - The `@dist` tier publishes the Orchestrator image to its registry with the other Kit images and drives the installed
170
+ CLI through a publish, so `PUT /assembly`, the stripping rule and the Kit version refusal execute there. The e2e
171
+ fixture boots the same entrypoint as a host process and publishes its instance through `PUT /assembly` with the
172
+ publish token it booted with, so every default-profile scenario crosses the door, and a restart boots from `current`.
173
+ The `@kind` tier builds the Orchestrator image in checkout mode as it builds the Harness.
174
+ - The GLOSSARY gains **Assembly**; **Kit version** becomes "the version the Instance pins, which is the tag of every Kit
175
+ image it deploys, the Orchestrator's own included"; "instance image" joins Kit image's avoid list.
176
+ - `RunBlob` gains the Assembly hash; a blob without one cannot be vouched for and is drift, as a blob without a shape
177
+ stamp was in [ADR-0030](0030-a-snapshot-names-the-machine-it-was-written-under.md). Greenfield: a stale volume is
178
+ deleted, not migrated.
179
+ - Many Assemblies held means many module trees in one process. They share the Kit's packages by the resolution rule, so
180
+ no identity problem; they are distinct directories, so Node's module cache keeps them apart.
@@ -0,0 +1,323 @@
1
+ # A Machine lends its parts to a Chart
2
+
3
+ The ask behind [ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md) had a second
4
+ half: an Agent should be able to create a powerful workflow at run time and run it, without a commit to the Instance and
5
+ without a human redeploying anything, and it must not be able to take the system over. The first half settled that only
6
+ `jr2 up` publishes code, because a Machine is TypeScript the Orchestrator imports into a process that holds the
7
+ ServiceAccount, the signing key and the Instance token, and every fence jr2 built keeps the Agent away from that
8
+ process: it reaches its Machine through a container it cannot read
9
+ ([ADR-0013](0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md)), holds Stand-ins and never keys
10
+ ([ADR-0059](0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md)), and spends a credential only through
11
+ an Effector ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)). One `fromPromise` written by
12
+ an Agent and imported by the Orchestrator collapses all of them. So an Agent's workflow cannot be code. The grill of
13
+ 2026-10-18 found that it does not need to be, and the prior art agrees: Step Functions keeps every `Resource` a constant
14
+ and puts all authority in the role; Argo's answer to "anyone who can submit a Workflow gets any ServiceAccount" was a
15
+ strict mode where a submitter may only reference a human-owned template; Temporal's own answer to "dynamic" is a trusted
16
+ interpreter walking untrusted JSON; Stately's v6 serializer stores a machine as data with implementations by name and
17
+ refuses inline code outright. In-process isolates (`node:vm`, `isolated-vm`, QuickJS in wasm) are argued against by
18
+ their own maintainers, and the escapes shipped in systems that used them were in the bridge.
19
+
20
+ ## Decision
21
+
22
+ - **A Chart is a Machine written as data alone.** States, transitions, invokes, `after` delays, nested and parallel
23
+ states, a final state's output: xstate's own JSON-serializable config, with every guard, action and delay named
24
+ (`{ type, params }`) and every `invoke.src` a slot name. It carries no function, no import, no secret name, no url, no
25
+ argv, no image and no Repo, and it names no Turn's placement: the Open body places every Agent Turn its Chart starts
26
+ from its own input, as its wrapper placed it (the stub tier's explicit `endpoint`, or the ambient `workspace()`). The
27
+ grammar is version 1 of a kit schema, `chartSchema`, not xstate's config whole: a state's own `onDone`, a parallel
28
+ join and an `emit` action are not in it, a nested final state's output is refused because nothing reads it, and a
29
+ Chart is at most 200 states, 8 deep. A spike against xstate 5.32.2 bound such a chart as a child of a hand-written
30
+ wrapper, with lent actors, named guards and actions reading `params`, `sendParent`, `onDone` output and a snapshot
31
+ round-trip.
32
+ - **An Open body is the slot a Chart binds, and a Chart wrapper is the Machine that declares it.** A Machine declares a
33
+ body slot and leaves it Open for a run to bind, the one Open part that a run, not a composer, binds
34
+ ([ADR-0054](0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)), as a per-run Repo Slot is
35
+ bound from the door ([ADR-0051](0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md)). The declaration
36
+ states three things: what it **lends**, by slot name (Agents, Effects, child Machines, guards, actions, delays); what
37
+ a Chart may **define** (the models and the Workspace access its own Agents may have); and the **ceilings** (below).
38
+ Nothing a Chart names resolves anywhere but there. "Wrapper" unqualified keeps
39
+ [ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md)'s sense, any Machine with a body slot,
40
+ `workspace()` and `pool()` included; a Chart wrapper is the one whose body a run binds.
41
+ - **A Chart mints routing, events and Agents; it borrows reach.** It mints its states and transitions; its Vocabulary,
42
+ each event `{ name, input: <JSON Schema>, audience? }`, built into zod at admission (zod 4 reads JSON Schema natively,
43
+ and the wire to the Harness was JSON Schema already), so a lent Agent's Menu derives from the Chart's events as from
44
+ any nested Machine's. The payload schema is a named subset: `type`, `properties`, `required`, `items`, `enum`,
45
+ `const`, `additionalProperties`, the numeric and length bounds, `title` and `description`; `pattern`, `$ref`, the
46
+ combinators and the conditionals are refused by name, because the Orchestrator checks every pick against the schema in
47
+ its own process, and a payload field named `type` is refused, because the event name is a constant the Orchestrator
48
+ writes after the payload. Its Gates it BORROWS: the kit's Gate is reach to the outside (a human's or a webhook's
49
+ answer into the run), so the Open body lends it whole, `lends.gate: true`, and a body that does not has no `gate` in
50
+ its grammar and refuses one at the door; a lent Gate's ids sit below the body's own actor path, so a Chart cannot take
51
+ the name of its wrapper's Gate. (Measured: with `gate` offered beside one lent Agent, three of five missed second
52
+ Charts reached for it as a worker.) And it mints its own Agents, because an Agent is five plain fields
53
+ ([ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md)) and the Harness runs whatever definition
54
+ rides the Turn. A minted Agent's model must be one the Open body allows and the Assembly placed a key for;
55
+ `workspace: "none"` needs the Instance Harness the Assembly converged. Both are door refusals, never a 401 mid-run. It
56
+ borrows, by slot name only, every Effect (the url, argv and secret were bound once by the author and the Chart fills
57
+ only its input slots), every Repo and image, every secret, every child Machine, and every guard, action or delay
58
+ beyond the generic set. Lending is attenuated by construction; an author who lends
59
+ `command(["sh", "-c", arg("script")])` has lent a shell, and that is theirs to choose. A lent name may not be dotted,
60
+ `gate`, `fail`, a generic action's name or an integer, because those are the kit's; the Gate itself is lent as
61
+ `gate: true`, and the Part that fails the run as `fail: true`
62
+ ([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)), because the Machine
63
+ decides which ends a Chart can choose. The guard `jr2.failure`, which tells a Failure from a defect on an `onError`,
64
+ is in every Chart, as `jr2.sentBy` is: it is a predicate and gives no capability.
65
+ - **Constants for authority, references for data.** `invoke.src`, guard and action `type`s, delay names and event names
66
+ are constants, checked against the lent set and the Chart's own declarations at admission. Data flows through
67
+ references: a placeholder `{{context.ticket.title}}`, or `{ "$ref": "event.summary" }`, in an invoke's `input`, an
68
+ action's `params`, a Gate's `meta` and the root `output`. A reference is a **path lookup** that jr2's own code
69
+ performs at invoke time: a list of property names walked on the run's context, event or input, which are plain data by
70
+ [ADR-0007](0007-durable-machine-state.md). There is no evaluation, no operator, no call; `{{context.round + 1}}` fails
71
+ the grammar. The roots are `context`, `event` and `input`, and nothing else; `event.output` and `event.error` are read
72
+ whole, and `event.error` is a line jr2 writes that names the invoke, never the error's own message, because a child's
73
+ error is a defect whose text never reaches a model
74
+ ([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)). A placeholder is read
75
+ from the Chart's own strings and never from data: a ticket body that contains `{{context.notes}}` lands in a prompt as
76
+ those characters; a lone placeholder yields the value itself, and one inside a string writes an object as JSON. Four
77
+ generic actions exist, `set { path, from }`, `append`, `increment` and `clear`, with `path` relative to the context;
78
+ everything else a Chart does is states, named guards and lent actions. The kit's own `task` builds its coder prompt
79
+ with a conditional on the round; in a Chart that is two states and a guard. A reference the door admitted but the
80
+ run's data lacks (a read before the state that writes it) settles the body `refused` with the reference named, never
81
+ as an error. A terminating, side-effect-free expression (CEL-class) is the named later addition, as a new reference
82
+ kind: adding one breaks no Chart, removing one would.
83
+ - **A Chart always runs under a Chart wrapper, and the Chart is the body actor's input.** Four doors, nothing else:
84
+
85
+ | Who | One-shot | Named |
86
+ | ------------------------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
87
+ | HTTP (a human, a tool, an MCP) | `POST /workflows/<wrapper>/runs` with `chart` in the input | `PUT /workflows/<wrapper>/charts/<name>` once, then `POST /workflows/<wrapper>/charts/<name>/runs` |
88
+ | A Machine (an Agent) | a pick carries the Chart; a state invokes the Open body with it | the Open body's input carries `{ chart: { name } }`, the wrapper's own Chart by its short name, resolved at invoke |
89
+
90
+ One-shot, the Chart wrapper is the Workflow whose run it is. Named, the Chart is a child of its wrapper: its Workflow
91
+ name is `<wrapper>/<name>`, its routes live under the wrapper's, and `jr2 run task/review-loop` starts it as the
92
+ ordinary run-start does. A Chart never runs on its own: the Open body it binds is where its lent parts and ceilings
93
+ come from. Either way the Chart arrives as the body actor's input, so the grammar reaches a planner as the JSON Schema
94
+ of its pick for free (`chartSchema` is exported by `@jr2/orchestrator`, and `chartSchemaFor(declaration)` is the same
95
+ grammar narrowed to one Open body, for a planner's pick), **narrowed per wrapper**:
96
+ `GET /workflows/<wrapper>/chart-schema` is the grammar with the wrapper's lent slot names, guard, action and delay
97
+ names, and allowed models as `enum`s, derived from the two facts jr2 already reads. A human writing
98
+ `workflows/<wrapper>/<name>.chart.json` points an editor at it and gets validation and completion; a planner whose
99
+ pick is `{ chart }` gets it as its tool parameters, so the model is constrained at generation time, which is as close
100
+ to a type as a model can get. A Chart file is admitted by `jr2 up`'s host-side walk with the same door, so its errors
101
+ surface at the same command as `tsc`'s, one step later. That is what compile-time type safety becomes for a Chart: the
102
+ wrapper stays TypeScript and typechecked, and the Chart's references into it are checked at a door that sees the live
103
+ lent set, which a compiler would not. And a name is resolved to text at invoke time and admitted exactly as text would
104
+ be, and **a name is a binding**: a named Chart was created against one Chart wrapper and runs under that wrapper only,
105
+ which its name and its path both say. So the by-name path takes the short name alone, `review-loop`, and resolves it
106
+ among the Charts of the wrapper that is the run's root Workflow, seen through a transparent wrapper; a Chart wrapper
107
+ that another Machine invokes as a child resolves no name, because the run is not that wrapper's, and there is no way
108
+ to write another wrapper's. Portability belongs to the text alone: to run one Chart under another wrapper, pass the
109
+ text one-shot or publish it under that wrapper too. The body actor constructs the live Machine from the text at start
110
+ and again at restore, which is [ADR-0007](0007-durable-machine-state.md)'s own rule: the text and its hash persist in
111
+ the body's own snapshot, so the run holds what it ran even if the name is later deleted or republished, and the run's
112
+ shape stamp ([ADR-0030](0030-a-snapshot-names-the-machine-it-was-written-under.md)) stays its wrapper's, because the
113
+ Chart is the run's data and not its Machine's shape. At restore the body admits its text again against the parts lent
114
+ now and compiles it again; a hash that does not match the text, or a text the door now refuses, is drift naming the
115
+ body slot and the reason. A one-shot Chart lives nowhere but its run, and
116
+ [ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)'s pin covers it by
117
+ construction. Resolving by name is invoke-by-name, which
118
+ [ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md) rejected for Machines; a name here resolves to
119
+ data the Open body still binds, so nothing that decision protects is lost.
120
+
121
+ - **A Chart's author is an Agent, and jr2 briefs it on the Turn that may write one.** A Chart is written by a model at
122
+ run time: a planner whose pick carries one. A human who keeps a Chart file reads the same grammar through an editor,
123
+ and is the second audience, not the first. The narrowed grammar constrains the Chart's names, but it does not teach
124
+ what a Chart IS, and a measured planner guessed xstate: `onDone` on an Agent, no `events`, the lent Agent redefined
125
+ under `agents`, `input.change` for a prompt, `event.result.value` for a pick's field. Its first Chart drew a ten-line
126
+ report and admitted on the third or fourth. So jr2 says how a Chart runs BEFORE the first Chart, as the **Chart
127
+ briefing** (`CHART_BRIEFING`, `chartBriefing(narrow)`): one text, jr2's, naming no task and no pick, that reaches
128
+ every author by one channel each. It is the grammar's root `description`, so `GET /workflows/<wrapper>/chart-schema`
129
+ and a pick's tool parameters carry it, and a future MCP built on the schema carries it for free. A narrowed grammar's
130
+ briefing names the lent Agents, and a body that allows no model has NO `agents` in its grammar and a briefing that
131
+ says so: a planner reads a field it cannot fill as one to fill (measured: eight of ten first Charts minted under a
132
+ `never` model, and read "must not have additional properties" for it), and a Chart that brings `agents` anyway reads
133
+ at the door why there is none. And the Agent's surface MARKS a pick whose parameter is a Chart with that grammar's
134
+ briefing (`SurfaceEvent.briefing`, by schema identity), so the Harness says the text in the Turn part, right before
135
+ "Allowed now", on every Turn where that pick is Allowed, and on a restatement that opens it
136
+ ([ADR-0062](0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md)). Nowhere else: the standing Briefing stays
137
+ Chart-agnostic, the tools block stays the same bytes on every Turn
138
+ ([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md)), and a seat whose Menu
139
+ holds no Chart pick reads not a word of it. The cost is the author's to scope: a seat whose Menu holds a Chart pick
140
+ carries the grammar as cached tool parameters on every Turn, so an author who wants a planner that only sometimes
141
+ writes a Chart gives the pick to a seat of its own. The grammar's fields keep terse sentences at the places a planner
142
+ got wrong (`agents`, `invoke.input`, `invoke.onDone`, `events`, `output`), because a model reads a property's
143
+ description as it emits the property. The door's report stays complete, one line per fault, as the second teacher. And
144
+ a narrowed grammar shows ONE worked fragment, as JSON Schema `examples` on the state definition and on `events`: an
145
+ Agent state invoking the body's first lent Agent, and the declared pick with a `required` field that ends its Turn,
146
+ handled in `on` with a `target` and a `set`. It is the one shape every Agent state must have to run, so it steers no
147
+ path: not the number of states, their order, the prompts, loops, guards or minted Agents. A whole Chart as an example
148
+ was refused for that reason, since the instrument copies what it is shown (it wrote the briefing's `set` verbatim),
149
+ and a Chart template in the grammar would make every Chart that Chart. A body that lends no Agent shows nothing: there
150
+ is no real name. `examples` is advisory in JSON Schema and some providers strip it from tool parameters, so it is a
151
+ lever for an instrument that passes the schema through, never a contract.
152
+
153
+ - **A Chart under a name is a Workflow and a child of its wrapper, from one of two origins.** Its name is
154
+ `<wrapper>/<name>`, one rule for both, and `<name>` is lower-case letters, digits and hyphens, 63 at most. A **Chart
155
+ file** in the Assembly sits at `workflows/<wrapper>/<name>.chart.json` and carries the Chart alone: discovered one
156
+ level below the flat `workflows/*.ts` of
157
+ [ADR-0050](0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md), in a directory named after its
158
+ wrapper and holding nothing else jr2 reads (a `_` or `.` directory is a helper's, as ADR-0050 has it, and a
159
+ `.chart.json` at the flat level is a warning, never a Chart). Another file in such a directory is a warning too,
160
+ except in a docker context that a registered Machine names: a module ships its context beside it
161
+ ([ADR-0037](0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md),
162
+ [ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md)), so `import.meta.resolve("./python")` in
163
+ `workflows/pytest.ts` is `workflows/python/`, a folder jr2 reads, and the warning would be false. A Chart file is
164
+ published by `jr2 up`, which admits every Chart file on the host before any cluster work, reproducible from the
165
+ Instance repo, admitted by the same static door at the publish. A **runtime Chart** is
166
+ `PUT /workflows/<wrapper>/charts/<name>` and `DELETE` on the same path, the body the Chart alone, behind the Instance
167
+ token, not the publish token, because a Chart is data and cannot reach the process, which is the whole point of this
168
+ decision; it lives on the Orchestrator's volume at `.jr2/charts/<wrapper>/<name>.chart.json` beside the Assemblies,
169
+ written durably, and survives restarts. A live run holds its Chart through the text in its own snapshot, so a `DELETE`
170
+ while a run is live answers at once and the run goes on. It lists in `jr2 runs` and the Console with its origin shown:
171
+ `jr2 runs` keeps its result, the live runs, and prints the listing beside it as activity, with `--workflows` to make
172
+ the listing the result ([ADR-0009](0009-cli-and-instance-interface.md)); there is no verb for `PUT` or `DELETE`, an
173
+ MCP or `curl` is the client. `GET /workflows` lists each wrapper with its Charts inside its entry, not beside it, and
174
+ `GET /workflows/<wrapper>` carries each Open body's declaration. So a Workflow is one of three things: a Machine from
175
+ the Assembly, a Chart file from the Assembly, or a runtime Chart, and the last two are children of the first. **There
176
+ is no collision rule between a Machine and a Chart**: a Machine's name is a filename stem and holds no slash, a
177
+ Chart's name always holds one, and a wrapper missing from a new Assembly makes its Charts unrunnable by construction,
178
+ which the publish reports. Between the two origins there is one: a Chart file and a runtime Chart of one name are the
179
+ file's, and the runtime Chart is unrunnable, kept, until the file leaves the Assembly; a `PUT` to a name a file holds
180
+ is refused. Rejected: flat names with a collision refusal and the wrapper named in the `PUT` body, the shape this
181
+ decision first took; the binding was then a rule to check in two places instead of a fact the name and the path state.
182
+ **jr2 tells a Chart wrapper from two facts it already reads**, and the author marks nothing: the walk sees the Open
183
+ body on the registered Machine (a kit constructor in `actors`, as `agent()` is, seen through a transparent wrapper's
184
+ body), and the door schema ([ADR-0033](0033-a-machine-declares-the-input-that-starts-it.md)) shows which field is
185
+ `chartSchema`. A wrapper whose door has no Chart field is a wrapper for in-Machine Charts only, run by `jr2 run` like
186
+ any Workflow; one with a Chart field can also be run one-shot by HTTP and can have named Charts. `GET /workflows`
187
+ lists both facts. A named Chart's fixed input supplies every Chart-typed field of the wrapper's door and nothing else,
188
+ so a named Chart stays a Chart and not a preset of a Workflow's door, which would be a decision of its own; a Machine
189
+ with two Open bodies has two Chart fields and a named Chart fixes both, and a Chart, named or a field, is admitted
190
+ when one of the bodies admits it, each report line naming its body, because the door cannot tell which body a field
191
+ feeds. A `PUT` or a Chart file under a wrapper with no Chart door is refused, as is one under a Workflow that is no
192
+ wrapper at all. **An Assembly publish re-admits every named Chart** against the new Assembly, because a lent slot may
193
+ have gone: a Chart file that fails refuses the publish, since the file is the Instance's own; a runtime Chart that
194
+ fails is marked unrunnable and reported in the publish's answer, never deleted, because it is someone else's. An
195
+ unrunnable Chart still lists, with its reason, and a start of it answers 409. An MCP that lets a user's own agent
196
+ author and run Charts is a client of these routes plus `GET /workflows/<wrapper>` for the Open body's declaration and
197
+ `GET /runs/<id>/events`; whether the kit ships one is a product question, not this one.
198
+ - **Admission is static and mandatory, because xstate refuses nothing.** The spike found that an un-lent `invoke.src`
199
+ kills the WRAPPER actor with a `TypeError` that bypasses the body invoke's `onError`; an unknown action is silently
200
+ ignored, even in the development build; an unknown delay name fires at once; an unguarded `always` cycle exhausted the
201
+ heap. So the door checks, before `createActor`, in this order: the size cap, then the grammar, and if either fails
202
+ nothing else is read; then every fault of the rules that follow, reported together, each line with its path in the
203
+ Chart and what is lent or allowed. The names: every `src`, `type` and delay name against the lent set and the Chart's
204
+ own `agents`, and a delay below the wall clock. The events: every `on` key against the Chart's events and the events
205
+ against a handling state ([ADR-0011](0011-workflow-defined-events.md)'s rules), each payload within the schema subset
206
+ above. The minted Agents: every model against the allowed list and the placed keys, every Workspace access against the
207
+ allowed set. The structure: no `always` cycle without a guard, through parallel regions and history too; no `onDone`
208
+ on an Agent invoke, because a Turn never settles by itself, it ends on a pick in `on`; no Agent state that nothing can
209
+ end; no parallel state at or below an Agent's. An Agent state of a Chart needs no `agent.fault` route. A state of a
210
+ Machine needs one ([ADR-0074](0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md)). An
211
+ `agent.fault` that no transition of the Chart takes goes, unchanged, to the Machine that invoked the Open body, and
212
+ that Machine's `agent.fault` route takes it, as it takes the fault of an Agent of its own. So a Machine that invokes
213
+ an Open body that can run an Agent needs the route too, on that state or above it. A route in the Chart stays legal,
214
+ and it can read `{{event.reason}}`. The wrapper is the right place: a Chart under a body that lends no `fail` has no
215
+ correct end for a fault, because a final state means `done`
216
+ ([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)) and a state that waits
217
+ holds the run until the wall clock. So the briefing does not teach the route, and the grammar's fragment does not show
218
+ it. And two checks that stand in for the types a Chart does not have. **Dataflow**: every reference a Chart makes,
219
+ `{{context.summary}}` or `{ "$ref": "event.notes" }`, must be supplied by something the door can see: the Chart's
220
+ initial `context`, the Chart's input (the wrapper's door less its Chart fields and the placement, or, for a body a
221
+ Machine invokes, what that invoke sends), the schema of the event the transition handles, or a `set`, `append` or
222
+ `increment` on that path or above it, where a `set` from one reference supplies what that reference supplies and
223
+ nothing deeper; a reference nothing supplies would resolve to an empty string in a prompt with no one the wiser, so it
224
+ is refused. The fields a Chart declares are its types. **Lent input shapes**: an Effect carries an `input` schema and
225
+ an Agent's Turn input is a fixed key set (`prompt`, `cwd`, `continue`, `model`, `thinkingLevel`, no placement), so the
226
+ literal parts of a Chart's `input` objects are validated at the door and the resolved object again at invoke time,
227
+ where a failure is that invoke's error and never a mid-Turn surprise. Last, the door compiles the Chart as the body
228
+ would: `jr2Setup`'s own refusals (less its `agent.fault` refusal), and the load warnings a hand-written Machine gets
229
+ (an unentered mode), are refusal lines for a Chart, because a planner reads a report and nobody reads a warning. A
230
+ refusal is the body's OUTPUT, `{ outcome: "refused", report }`, routed by the wrapper's `onDone` with a guard on
231
+ `outcome`, so a wrapper whose `onDone` returns to the planning state hands it to the planner as the output link in its
232
+ Chain ([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)): "Chart refused:
233
+ `src: "linter"` is not a lent actor; lent: coder, reviewer, push", and the planner answers with a corrected Chart. It
234
+ is not the body's error, because a child's error is a defect that never reaches the model (ADR-0067, which stands); at
235
+ the HTTP door it is 422 with the same report. No new mechanism: an Agent reads its children's outputs through the
236
+ Chain already.
237
+ - **Counters are the Open body's.** Its declaration states the Chart's ceilings: Turns, every Agent Turn below the body,
238
+ a lent Machine's included; wall clock, at most xstate's timer bound of 2^31-1 ms; transitions, one microstep budget
239
+ for the whole run, since xstate has no loop guard; fan-out width, the children live at once; and Chart size in bytes,
240
+ at most 1 MiB. One bound is the kit's and not the declaration's: a context or an output above 4 MiB. A ceiling met
241
+ settles the body `{ outcome: "ceiling", ceiling }`, its children stopped, and a Turn one past the Turns ceiling never
242
+ starts. A spend ceiling checked before each model call is the right next one; jr2 meters no spend today, so it is
243
+ named and deferred.
244
+ - **The Console renders the run's Machine.** Every machine doc came from the definition, so a Chart's states would not
245
+ render. A per-run doc route exists, `GET /workflows/<name>/runs/<id>/machine`, and the run view draws the bound
246
+ Machine, keyed by the Charts the run binds now, so a planner's second Chart draws a second doc; a Chart the run cannot
247
+ draw (a drifted run) is named in the doc. A Chart one cannot watch is a Chart one cannot trust.
248
+ - **The trust statement.** Code is published by the Instance; a run supplies only data. The fence is structural, so it
249
+ holds whether the Agent is prompt-injected or the model is adversarial: data cannot reach the process, and a borrowed
250
+ Effect reaches only the host its author fixed. What remains is cost and routing abuse, which the counters bound, and a
251
+ Gate the Chart itself defines being approved on bad information, which is the author's Gate placement to decide.
252
+
253
+ ## Considered options
254
+
255
+ - **Code, and trust the publisher**: an Agent publishes a Machine through an Effect holding the Instance token, behind a
256
+ human Gate. Rejected: the Gate is the only fence, on a process that holds the cluster, and a reviewer reading
257
+ generated code under time is not a fence
258
+ [ADR-0059](0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md) would accept anywhere else.
259
+ - **Isolation**: a dynamic body's logic in an isolate or a worker with no secrets. Rejected: Node disclaims `vm` as a
260
+ security mechanism, `isolated-vm` is in maintenance mode with a bridge its author calls trivially crossed, QuickJS in
261
+ wasm is unaudited; the run host is in-process xstate, so this is a second Orchestrator and a coordination protocol;
262
+ and it buys a generality the body does not need.
263
+ - **Composition only, no minted Agents.** Rejected: an Agent is honestly data, and a Chart that cannot say "a tester
264
+ with these instructions" is not powerful enough to be worth the door.
265
+ - **An expression language from the start.** Rejected above: it is [ADR-0008](0008-orchestrator-as-deployed-app.md)'s
266
+ rejected DSL arriving through a side door; a reference kind can be added later without breaking a written Chart.
267
+ - **One-shot only, no Chart under a name.** Rejected: a test wants the same Chart on every run (the text already gives
268
+ that), but a person, another tool and an MCP want `jr2 run <name>`, a Console entry and a Chart that outlives the run
269
+ that wrote it; and a Chart under a name is a Workflow registered as DATA, which keeps
270
+ [ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)'s one publisher of CODE
271
+ true.
272
+ - **A Chart as an anonymous run with no Chart wrapper.** Rejected: every fence above is the wrapper's; and the run would
273
+ have no Workflow name for `jr2 status`, drift reporting and the Console.
274
+ - **A kit Effect that publishes a Chart from a Machine** (`publishChart`, over the Effector's signed wire, no token).
275
+ Deferred: nothing needs an Agent to keep a Chart yet; a Machine that wants one later runs it by name after a human or
276
+ a tool published it. If it comes, it is an Effect the author writes in, with or without a Gate, and never a side
277
+ effect of running an Open body.
278
+
279
+ ## Consequences
280
+
281
+ - [ADR-0008](0008-orchestrator-as-deployed-app.md)'s "code, not config" is restated, not reversed: a human writes code;
282
+ what a run supplies is data. Its "dynamic plugin loading stays deferred" line retires: code is never loaded
283
+ dynamically, and a run may bind an Open body with a Chart.
284
+ - The walk learns a second mode. Today it is not `provide()`-aware and reads nothing a per-run binding adds; the door's
285
+ walk runs over the bound Chart, and the `jr2 up` walk reads an Open body's declaration (its lent parts, allowed
286
+ models, ceilings) so the keys and the Instance Harness a Chart could need are placed before any run: a body that lends
287
+ an Agent with a Workspace puts its allowed models on the pods, and one that allows `"none"` on the Instance Harness,
288
+ which is converged for it.
289
+ - `fingerprintOf` stays the wrapper's; the Chart's text and hash ride the body's own snapshot inside `RunBlob`, and
290
+ restore admits and compiles the text again. A Chart that no longer passes the door at restore is drift with that
291
+ reason. Under [ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md) a live run
292
+ holds its Assembly and a publish takes no lent slot from it, so this drift guards a Kit change or a damaged volume,
293
+ not a later publish.
294
+ - The GLOSSARY gains **Chart**, **Chart wrapper**, **Open body** and **Reference**; **Workflow** gains its three
295
+ origins; **Open** no longer says a run never sees one. The Briefing's Turn part gains a pick's briefing
296
+ ([ADR-0062](0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md) as amended): the Chart briefing is its first.
297
+ - The `@model` tier gains the claim `chart-refusal`: a planner whose pick is `run_chart` with the narrowed grammar is
298
+ asked for a Chart that names a slot the body does not lend, the door refuses it, and the planner's next Chart admits.
299
+ A Chart's prompts are its author's, so the claim measures jr2's text: the Chart briefing and the door's report. The
300
+ floor is 7 of 10, set ahead of a measured run. Measured 2026-10-19 on the home-lab instrument, one lever per run of
301
+ ten: with no briefing, 0, 4 and 2 trials (the first report was about twelve lines; the second Chart fixed most and not
302
+ all); with the briefing and a body that allowed no model, 0 (the first report fell to a median of three lines, but
303
+ eight first Charts minted an Agent under a `never` model and the measured pick hit the Menu's "must not have
304
+ additional properties"); with `agents` gone from that grammar, 1 of 10 (the second Chart's fault was now one line,
305
+ "context.summary is supplied by nothing", in six of nine, and every one of them WAS written, from a pick field the
306
+ Chart never put in `required`, which the line hid); with that line naming the write and the field, and the briefing
307
+ teaching `required`, 5 of 10, and every miss admitted on the third Chart; three of the five misses reached for the
308
+ kit's `gate`, offered in the `src` enum beside the one lent Agent, and the rest of the noise was the Chart sent as a
309
+ JSON string, which pi's validation refused ("chart: must be object") twelve times in ten trials. With the Gate a lend,
310
+ 4 of 10 and no `gate` fault; in three of the six misses the measured pick was that string refusal, twenty times in ten
311
+ trials. The next run, otherwise unchanged, gave 7 of 10: the floor, and the baseline is written from that read run,
312
+ with the reading that the gain from 4 is the instrument's own variance across ten trials. A parse of the JSON-text
313
+ argument was tried in that run and measured inert, since every such string was malformed
314
+ ([ADR-0029](0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md), rejected). The misses of
315
+ the passing run were a Chart without `events`, a state `output` beside the Chart's, and one malformed string. A
316
+ thinking-on cell, run once to ask whether the misses are knowledge or care: 5 of 10 with three void trials (the first
317
+ Chart admitted more often), the same kinds of miss (events declared and handled by no state, `gate` named though no
318
+ grammar offered it), 8% longer per trial and a third more wall time for the voids. Not kept: it does not clear the
319
+ floor, and it answers the question: the misses are knowledge. With the worked fragment in the narrowed grammar, 9 of
320
+ 10 with no void trial, in 15 minutes against 23: the one miss was a malformed string, the first reports fell to a
321
+ median of two lines, and the ten second Charts kept their own state and event names (two of ten reused the fragment's
322
+ `finished`). The baseline is that run's. What the next Chart gets wrong is in the trial evidence.
323
+ - Deferred, named: a spend ceiling; a CEL-class reference kind; `publishChart`.