@jr2/orchestrator 0.4.1 → 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.
- package/README.md +20 -2
- package/console/components/app.ts +2 -0
- package/console/components/fleet.ts +14 -1
- package/console/components/machine-pane.ts +41 -2
- package/console/main.ts +13 -1
- package/console/store.ts +102 -6
- package/console/style.css +21 -0
- package/docs/agents-block.md +5 -0
- package/docs/concepts/agents-and-turns.md +442 -0
- package/docs/concepts/architecture.md +230 -0
- package/docs/concepts/charts.md +143 -0
- package/docs/concepts/composition.md +357 -0
- package/docs/concepts/converge.md +254 -0
- package/docs/concepts/durable-runs.md +189 -0
- package/docs/concepts/effects.md +603 -0
- package/docs/concepts/gates.md +208 -0
- package/docs/concepts/machines-and-workflows.md +289 -0
- package/docs/concepts/pools-and-sources.md +230 -0
- package/docs/concepts/secrets-and-trust.md +222 -0
- package/docs/concepts/the-menu.md +378 -0
- package/docs/concepts/workspaces.md +678 -0
- package/docs/get-started/a-model-decides.md +563 -0
- package/docs/get-started/an-agent-in-a-sandbox.md +381 -0
- package/docs/get-started/connect-a-model.md +248 -0
- package/docs/get-started/create-an-instance.md +194 -0
- package/docs/get-started/have-your-assistant-write-a-machine.md +922 -0
- package/docs/get-started/install.md +126 -0
- package/docs/guides/add-tools-to-the-sandbox.md +295 -0
- package/docs/guides/answer-a-gate.md +304 -0
- package/docs/guides/bind-open-parts.md +223 -0
- package/docs/guides/cancel-a-run.md +146 -0
- package/docs/guides/clean-up.md +206 -0
- package/docs/guides/fix-a-failed-converge.md +361 -0
- package/docs/guides/give-the-cluster-git-credentials.md +222 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +190 -0
- package/docs/guides/let-an-agent-write-a-chart.md +226 -0
- package/docs/guides/look-inside-a-sandbox.md +174 -0
- package/docs/guides/mirror-the-kit-images.md +176 -0
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +302 -0
- package/docs/guides/open-the-console.md +91 -0
- package/docs/guides/publish-a-machine-as-an-npm-package.md +350 -0
- package/docs/guides/push-and-sign-a-branch.md +486 -0
- package/docs/guides/run-a-chart-by-name.md +270 -0
- package/docs/guides/run-a-command-or-call-a-url.md +544 -0
- package/docs/guides/run-one-worker-per-item.md +321 -0
- package/docs/guides/set-an-agents-access-and-dials.md +375 -0
- package/docs/guides/size-a-workspace.md +204 -0
- package/docs/guides/start-a-run-from-a-run.md +237 -0
- package/docs/guides/start-a-run.md +207 -0
- package/docs/guides/upgrade-the-kit-version.md +240 -0
- package/docs/guides/use-a-hosted-provider.md +208 -0
- package/docs/guides/use-a-local-model.md +313 -0
- package/docs/guides/watch-a-run.md +326 -0
- package/docs/guides/write-a-workflow.md +318 -0
- package/docs/index.md +44 -0
- package/docs/llms.txt +216 -0
- package/docs/reference/authoring-surface/agent.md +128 -0
- package/docs/reference/authoring-surface/answer.md +140 -0
- package/docs/reference/authoring-surface/arg.md +60 -0
- package/docs/reference/authoring-surface/basic.md +62 -0
- package/docs/reference/authoring-surface/bearer.md +53 -0
- package/docs/reference/authoring-surface/chartschema.md +23 -0
- package/docs/reference/authoring-surface/chartschemafor.md +48 -0
- package/docs/reference/authoring-surface/command.md +191 -0
- package/docs/reference/authoring-surface/customize.md +64 -0
- package/docs/reference/authoring-surface/defineconfig.md +41 -0
- package/docs/reference/authoring-surface/defineevent.md +114 -0
- package/docs/reference/authoring-surface/dispatch.md +126 -0
- package/docs/reference/authoring-surface/fail.md +83 -0
- package/docs/reference/authoring-surface/file.md +122 -0
- package/docs/reference/authoring-surface/frompick.md +277 -0
- package/docs/reference/authoring-surface/http.md +159 -0
- package/docs/reference/authoring-surface/isfailure.md +58 -0
- package/docs/reference/authoring-surface/jr2setup.md +237 -0
- package/docs/reference/authoring-surface/open.md +74 -0
- package/docs/reference/authoring-surface/openbody.md +86 -0
- package/docs/reference/authoring-surface/paramsof.md +125 -0
- package/docs/reference/authoring-surface/pool.md +186 -0
- package/docs/reference/authoring-surface/push.md +112 -0
- package/docs/reference/authoring-surface/secret.md +118 -0
- package/docs/reference/authoring-surface/sentby.md +83 -0
- package/docs/reference/authoring-surface/source.md +51 -0
- package/docs/reference/authoring-surface/text.md +62 -0
- package/docs/reference/authoring-surface/type-agentdeclaration.md +27 -0
- package/docs/reference/authoring-surface/type-agentdefinition.md +80 -0
- package/docs/reference/authoring-surface/type-agentlogic.md +15 -0
- package/docs/reference/authoring-surface/type-agentslots.md +12 -0
- package/docs/reference/authoring-surface/type-answeraction.md +12 -0
- package/docs/reference/authoring-surface/type-arg.md +19 -0
- package/docs/reference/authoring-surface/type-argnames.md +12 -0
- package/docs/reference/authoring-surface/type-binding.md +62 -0
- package/docs/reference/authoring-surface/type-bindingof.md +12 -0
- package/docs/reference/authoring-surface/type-bodyacceptsdoor.md +15 -0
- package/docs/reference/authoring-surface/type-bodyacceptsslots.md +17 -0
- package/docs/reference/authoring-surface/type-ceilings.md +41 -0
- package/docs/reference/authoring-surface/type-chainlink.md +19 -0
- package/docs/reference/authoring-surface/type-chartaction.md +46 -0
- package/docs/reference/authoring-surface/type-chartagent.md +28 -0
- package/docs/reference/authoring-surface/type-chartevent.md +20 -0
- package/docs/reference/authoring-surface/type-chartguard.md +20 -0
- package/docs/reference/authoring-surface/type-chartinvoke.md +28 -0
- package/docs/reference/authoring-surface/type-chartjson.md +14 -0
- package/docs/reference/authoring-surface/type-chartstate.md +46 -0
- package/docs/reference/authoring-surface/type-charttext.md +40 -0
- package/docs/reference/authoring-surface/type-charttransition.md +22 -0
- package/docs/reference/authoring-surface/type-charttransitions.md +14 -0
- package/docs/reference/authoring-surface/type-chartvalue.md +15 -0
- package/docs/reference/authoring-surface/type-childat.md +12 -0
- package/docs/reference/authoring-surface/type-childslots.md +12 -0
- package/docs/reference/authoring-surface/type-commandenv.md +15 -0
- package/docs/reference/authoring-surface/type-commandinput.md +19 -0
- package/docs/reference/authoring-surface/type-commandoptions.md +78 -0
- package/docs/reference/authoring-surface/type-commandoutput.md +104 -0
- package/docs/reference/authoring-surface/type-customize.md +109 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +39 -0
- package/docs/reference/authoring-surface/type-dispatchinput.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-dispatchoutput.md +65 -0
- package/docs/reference/authoring-surface/type-duration.md +15 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +93 -0
- package/docs/reference/authoring-surface/type-effectlogic.md +12 -0
- package/docs/reference/authoring-surface/type-envopenparts.md +14 -0
- package/docs/reference/authoring-surface/type-eventaudience.md +27 -0
- package/docs/reference/authoring-surface/type-eventdef.md +45 -0
- package/docs/reference/authoring-surface/type-eventfrom.md +16 -0
- package/docs/reference/authoring-surface/type-eventsemantics.md +26 -0
- package/docs/reference/authoring-surface/type-execoutcome.md +34 -0
- package/docs/reference/authoring-surface/type-failslot.md +14 -0
- package/docs/reference/authoring-surface/type-failure.md +19 -0
- package/docs/reference/authoring-surface/type-failureinput.md +20 -0
- package/docs/reference/authoring-surface/type-filebody.md +41 -0
- package/docs/reference/authoring-surface/type-frominput.md +14 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +58 -0
- package/docs/reference/authoring-surface/type-httpdefinition.md +101 -0
- package/docs/reference/authoring-surface/type-httpinput.md +12 -0
- package/docs/reference/authoring-surface/type-httpmethod.md +14 -0
- package/docs/reference/authoring-surface/type-httpopenparts.md +12 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +89 -0
- package/docs/reference/authoring-surface/type-httprequestparts.md +33 -0
- package/docs/reference/authoring-surface/type-jr2config.md +126 -0
- package/docs/reference/authoring-surface/type-lends.md +38 -0
- package/docs/reference/authoring-surface/type-logicat.md +12 -0
- package/docs/reference/authoring-surface/type-markedif.md +12 -0
- package/docs/reference/authoring-surface/type-mergedactors.md +12 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +25 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +26 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +32 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +40 -0
- package/docs/reference/authoring-surface/type-openbodylogic.md +14 -0
- package/docs/reference/authoring-surface/type-openbodyoutput.md +42 -0
- package/docs/reference/authoring-surface/type-openbodyslot.md +18 -0
- package/docs/reference/authoring-surface/type-opencommandinput.md +17 -0
- package/docs/reference/authoring-surface/type-openeffect.md +12 -0
- package/docs/reference/authoring-surface/type-openeffectslots.md +12 -0
- package/docs/reference/authoring-surface/type-openof.md +12 -0
- package/docs/reference/authoring-surface/type-opensecret.md +23 -0
- package/docs/reference/authoring-surface/type-opensecretmark.md +12 -0
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +31 -0
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +32 -0
- package/docs/reference/authoring-surface/type-poolmachine.md +28 -0
- package/docs/reference/authoring-surface/type-poolspec.md +60 -0
- package/docs/reference/authoring-surface/type-pushinput.md +15 -0
- package/docs/reference/authoring-surface/type-pushoptions.md +79 -0
- package/docs/reference/authoring-surface/type-pushoutput.md +124 -0
- package/docs/reference/authoring-surface/type-pushwireoutput.md +12 -0
- package/docs/reference/authoring-surface/type-quantity.md +14 -0
- package/docs/reference/authoring-surface/type-reached.md +12 -0
- package/docs/reference/authoring-surface/type-reposlot.md +70 -0
- package/docs/reference/authoring-surface/type-reposlotsof.md +16 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +77 -0
- package/docs/reference/authoring-surface/type-secret.md +25 -0
- package/docs/reference/authoring-surface/type-sender.md +19 -0
- package/docs/reference/authoring-surface/type-sentbyguards.md +12 -0
- package/docs/reference/authoring-surface/type-size.md +85 -0
- package/docs/reference/authoring-surface/type-slothandles.md +32 -0
- package/docs/reference/authoring-surface/type-sourcenextresult.md +17 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +44 -0
- package/docs/reference/authoring-surface/type-syncstate.md +12 -0
- package/docs/reference/authoring-surface/type-text.md +19 -0
- package/docs/reference/authoring-surface/type-textpart.md +24 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +39 -0
- package/docs/reference/authoring-surface/type-toparameterizedobject.md +14 -0
- package/docs/reference/authoring-surface/type-toprovidedactor.md +14 -0
- package/docs/reference/authoring-surface/type-unknown.md +21 -0
- package/docs/reference/authoring-surface/type-usercontainer.md +77 -0
- package/docs/reference/authoring-surface/type-withoutemptyindex.md +20 -0
- package/docs/reference/authoring-surface/type-workflowevent.md +46 -0
- package/docs/reference/authoring-surface/type-workspaceaccess.md +16 -0
- package/docs/reference/authoring-surface/type-workspaced.md +94 -0
- package/docs/reference/authoring-surface/type-workspacehandles.md +23 -0
- package/docs/reference/authoring-surface/type-workspacemachine.md +29 -0
- package/docs/reference/authoring-surface/type-workspaceof.md +13 -0
- package/docs/reference/authoring-surface/type-workspaceoptions.md +39 -0
- package/docs/reference/authoring-surface/type-workspacespec.md +37 -0
- package/docs/reference/authoring-surface/workspace.md +259 -0
- package/docs/reference/authoring-surface.md +197 -0
- package/docs/reference/cli.md +433 -0
- package/docs/reference/config.md +756 -0
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +113 -0
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +110 -0
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +124 -0
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +230 -0
- package/docs/reference/decisions/0006-agent-control-surface.md +47 -0
- package/docs/reference/decisions/0007-durable-machine-state.md +109 -0
- package/docs/reference/decisions/0008-orchestrator-as-deployed-app.md +60 -0
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +275 -0
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +160 -0
- package/docs/reference/decisions/0011-workflow-defined-events.md +141 -0
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +103 -0
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +157 -0
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +95 -0
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +265 -0
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +124 -0
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +69 -0
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +111 -0
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +275 -0
- package/docs/reference/decisions/0020-private-ca-trust-is-a-named-concept.md +60 -0
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +100 -0
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +103 -0
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +176 -0
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +439 -0
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +65 -0
- package/docs/reference/decisions/0026-a-turn-that-is-over-is-never-prompted.md +62 -0
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +237 -0
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +89 -0
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +154 -0
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +100 -0
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +190 -0
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +61 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +127 -0
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +57 -0
- package/docs/reference/decisions/0035-a-runaway-turn-is-ended-rerolled-once-then-a-fault.md +92 -0
- package/docs/reference/decisions/0036-a-turn-compacts-its-own-context-mid-flight.md +138 -0
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +196 -0
- package/docs/reference/decisions/0038-jr2-up-builds-every-image-it-deploys.md +170 -0
- package/docs/reference/decisions/0039-image-garbage-collects-by-reachability.md +115 -0
- package/docs/reference/decisions/0041-a-build-the-host-already-holds-is-not-spent-again.md +64 -0
- package/docs/reference/decisions/0042-ready-is-not-routable-an-admission-retries-its-connection.md +142 -0
- package/docs/reference/decisions/0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md +170 -0
- package/docs/reference/decisions/0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md +85 -0
- package/docs/reference/decisions/0045-the-platform-joins-the-image-address-and-the-cluster-chooses-it.md +82 -0
- package/docs/reference/decisions/0046-a-converge-failure-carries-its-diagnosis.md +34 -0
- package/docs/reference/decisions/0047-the-git-ssh-key-source-is-the-users-choice.md +47 -0
- package/docs/reference/decisions/0048-the-orchestrator-boots-without-its-repos.md +36 -0
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +126 -0
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +73 -0
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +227 -0
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +81 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +111 -0
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +126 -0
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +107 -0
- package/docs/reference/decisions/0056-a-global-jr2-hands-off-to-the-instances-own-and-an-instance-has-one-kit-version.md +62 -0
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +152 -0
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +106 -0
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +384 -0
- package/docs/reference/decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md +111 -0
- package/docs/reference/decisions/0061-a-memory-kill-ends-the-agents-processes-not-the-harness.md +83 -0
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +212 -0
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +83 -0
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +79 -0
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +65 -0
- 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
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +430 -0
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +464 -0
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +180 -0
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +323 -0
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +183 -0
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +144 -0
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +122 -0
- package/docs/reference/decisions/0074-an-unrouted-mechanism-event-is-refused-at-load-and-faults-the-run.md +76 -0
- package/docs/reference/decisions.md +83 -0
- package/docs/reference/glossary.md +648 -0
- package/docs/reference/http-api.md +1348 -0
- package/docs/reference/machines.md +190 -0
- package/docs/reference/mechanism-events.md +72 -0
- package/docs/why.md +206 -0
- package/package.json +5 -3
- package/src/actor.ts +541 -79
- package/src/agent.ts +114 -38
- package/src/ambient.ts +2 -1
- package/src/assembly-checks.ts +189 -7
- package/src/cause.ts +234 -0
- package/src/chart.ts +169 -37
- package/src/charts.ts +34 -6
- package/src/config.ts +430 -178
- package/src/custodian.ts +74 -22
- package/src/customize.ts +188 -46
- package/src/dispatch-effect.ts +945 -0
- package/src/door-compat.ts +217 -0
- package/src/durability.ts +11 -4
- package/src/effect.ts +341 -44
- package/src/effector-wire.ts +160 -11
- package/src/fail.ts +209 -0
- package/src/harness-client.ts +541 -47
- package/src/http-effect.ts +495 -53
- package/src/http.ts +427 -16
- package/src/index.ts +60 -82
- package/src/instance.ts +68 -8
- package/src/internal.ts +84 -0
- package/src/machine-doc.ts +21 -3
- package/src/mechanism-routes.ts +168 -0
- package/src/model-keys.generated.ts +53 -0
- package/src/model-keys.ts +18 -58
- package/src/notices.ts +66 -13
- package/src/open-body.ts +159 -23
- package/src/open-effect.ts +81 -8
- package/src/open.ts +59 -6
- package/src/parts.ts +184 -13
- package/src/party.ts +68 -2
- package/src/pick.ts +463 -11
- package/src/pool.ts +247 -36
- package/src/registration.ts +83 -4
- package/src/repos.ts +2 -1
- package/src/run-host.ts +565 -31
- package/src/run-output.ts +38 -0
- package/src/sandbox-effect.ts +273 -38
- package/src/sandbox-kube.ts +3 -1
- package/src/secret-placement.ts +52 -17
- package/src/server.ts +3 -0
- package/src/setup.ts +239 -56
- package/src/size.ts +121 -9
- package/src/snapshot-store.ts +54 -10
- package/src/stub-harness.ts +44 -16
- package/src/timers.ts +14 -1
- package/src/vocabulary.ts +69 -14
- package/src/wire.ts +90 -16
- package/src/workspace.ts +697 -175
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# The agent picks from a Machine-defined, schema-backed menu; it does not drive the workflow
|
|
2
|
+
|
|
3
|
+
The control plane is Menu tool calls (ADR-0002), but **which** events an Agent may emit is scoped to the current Machine
|
|
4
|
+
state and defined by the Machine, not an open set the Agent invents. The menu derives from the invoking state's
|
|
5
|
+
transitions (ADR-0015 — the consumer authors no tool lists), is read by the Harness through the pod's Custodian
|
|
6
|
+
(ADR-0013), and the Agent **chooses one** (controlled agency); the Machine owns the transition table. The Agent never
|
|
7
|
+
steers the workflow — it answers within a frame the Machine set — and because its only control-plane peer is the
|
|
8
|
+
Custodian (which holds a credential it cannot read), that frame is _enforced_, not advertised. Menus only change at turn
|
|
9
|
+
boundaries, and the Harness re-reads the Menu per Submission (ADR-0013/0027), so no `list_changed` push channel is
|
|
10
|
+
needed.
|
|
11
|
+
|
|
12
|
+
There is no jr2-blessed event vocabulary: every menu entry is a workflow-defined event (ADR-0011).
|
|
13
|
+
|
|
14
|
+
## Encode flat
|
|
15
|
+
|
|
16
|
+
A named event schema must compile to a flat tagged object (an `enum` discriminator + each option's params as optionals
|
|
17
|
+
|
|
18
|
+
- a validation `check`), **never a top-level `oneOf`/`anyOf`**. Measured (PoC #5b): a strict variant (→ `oneOf`/`const`
|
|
19
|
+
tool schema) is unsatisfiable for Qwen3-Coder — it serializes the tool args as a JSON string and validation fails on
|
|
20
|
+
every retry. A flat object is satisfied first try. A conditional contract ("`notes` required iff `request_changes`")
|
|
21
|
+
is enforced by jr2 after the pick, not by the tool schema.
|
|
22
|
+
|
|
23
|
+
## A pick declares its input, never its answer
|
|
24
|
+
|
|
25
|
+
A def states what the Agent supplies (`input`) and nothing about what comes back. Every pick is `ack`: delivered and
|
|
26
|
+
answered on the same call. A pick that moves the workflow to work inside the Agent's state is answered when that work is
|
|
27
|
+
done (ADR-0024), and the answer is the Machine's: the last reply since the pick, or what its `answer` action sets
|
|
28
|
+
(ADR-0067). So a def has no `output` schema and no `deferred` semantics; `defineEvent` refuses both. `poll` stays
|
|
29
|
+
reserved (ADR-0013).
|
|
30
|
+
|
|
31
|
+
## One path: every conversation lives on the agent wire
|
|
32
|
+
|
|
33
|
+
The wire has exactly one conversation surface (ADR-0027): `POST /agents/:name/:id`, resumable by `(name, iid) + offset`.
|
|
34
|
+
There is no separate "workflow run" surface, and nothing on the wire forces a structured result out of a turn — the menu
|
|
35
|
+
is the only structured channel. Consequences:
|
|
36
|
+
|
|
37
|
+
- **All continuing, multi-turn work lives on the agent path**, driven by `agentRun` (ADR-0016).
|
|
38
|
+
- **A turn that settles without a valid pick is re-prompted by jr2** — the budgeted no-signal nudge _inside_ `agentRun`
|
|
39
|
+
(ADR-0016). The workflow sees one terminal `agent.fault` on exhaustion.
|
|
40
|
+
- **Self-contained, single-shot decisions** (an LLM-judge / classifier / "given this diff + this rubric, return a
|
|
41
|
+
verdict") are one-turn conversations whose menu is the verdict — the same path, not a second mechanism.
|
|
42
|
+
|
|
43
|
+
## Residual
|
|
44
|
+
|
|
45
|
+
Unsolicited mid-turn steering remains the hard case (ADR-0002): the menu only opens when the Agent reaches a checkpoint
|
|
46
|
+
or a turn boundary. Evidence for the findings above: `poc/actor/POC-5B-FINDINGS.md`, `poc/actor/POC-5C-FINDINGS.md`
|
|
47
|
+
(`poc/` is gitignored; conclusions live here).
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Durable Machine state: serializable context, live things constructed from input
|
|
2
|
+
|
|
3
|
+
The Orchestrator is a forever-running daemon that must survive a `kill -9` and resume in-flight work, so every Machine
|
|
4
|
+
has to be reconstructable from a persisted xstate snapshot. That forces one invariant across everything that runs in a
|
|
5
|
+
Machine:
|
|
6
|
+
|
|
7
|
+
> Machine context and actor input hold **plain serializable data only** — ids, counters, status, data-shaped handles
|
|
8
|
+
> (e.g. a Sandbox's `endpoint` string). Live infrastructure — HTTP servers, clients, live actor references — is
|
|
9
|
+
> **constructed per-invocation from serializable input** (ADR-0011's static-import doctrine) and never persisted.
|
|
10
|
+
> Parent↔child coordination uses `sendParent` events plus serializable ids, never a held child-actor reference.
|
|
11
|
+
|
|
12
|
+
PoC #7 discovered this the hard way (a live `http.Server` in context crashed `JSON.stringify`); PoC #8 was designed to
|
|
13
|
+
it from the start (the top Machine keeps only counts + ids, children report completion UP via
|
|
14
|
+
`sendParent('FEATURE_DONE')`). They are the same invariant — #8's discipline is precisely what makes #7's
|
|
15
|
+
snapshot/restore possible.
|
|
16
|
+
|
|
17
|
+
The store's own durability — what a persist survives, and how long a finished run's row lives — is
|
|
18
|
+
[ADR-0065](0065-a-persist-survives-a-crash-and-history-is-a-week.md).
|
|
19
|
+
|
|
20
|
+
Durable **agent handles** do not ride Machine context at all: the host persists an `iid → admission` ledger beside the
|
|
21
|
+
snapshot in the same save (ADR-0016). Iids are globally unique, so restore needs no context-tree walking to find them.
|
|
22
|
+
An admission names the Turn ID of the Turn that ledgered it, because the continued Turns of one conversation share an
|
|
23
|
+
iid. A restore re-attaches only that Turn (ADR-0016).
|
|
24
|
+
|
|
25
|
+
**Effects** keep a **ledger** beside the snapshot in the same save (ADR-0067): one row per Effect, by its id. An
|
|
26
|
+
`intent` row is written before the call — saved at the write, so the snapshot that holds the Effect and the row land
|
|
27
|
+
together, before the request leaves; a `settled` row is written with the output, saved at the write before the
|
|
28
|
+
transition the output drives and again in the save that carries that transition; an `aborted` row when the Effect's
|
|
29
|
+
state exited while it ran, kept in memory and saved by the save that carries that exit (a cancel's final save included),
|
|
30
|
+
never by a save inside the transition. A restore reads the row and never guesses: settled → the output is delivered (the
|
|
31
|
+
transition's save was lost); intent → per kind: a command that names no secret re-attaches to its exec by id, or settles
|
|
32
|
+
`unknown` when the Harness no longer knows it; an http call (a Worktree file body included) asks every replica of its
|
|
33
|
+
Effector for its id and follows the one that holds it, and when none does it is sent again only when the author marked
|
|
34
|
+
it `idempotent`, else it settles `unknown`; a push and a credentialed command ask their Sandbox Effector for their id,
|
|
35
|
+
and when it no longer holds them are sent again with only the commit the intent row pinned — never the Agent's tip now —
|
|
36
|
+
or settle `unknown` when no commit was pinned; no row → nothing was sent, so it starts. jr2 runs an Effect it started a
|
|
37
|
+
second time only where its author marked it `idempotent`, or where its run is a function of a pinned commit (a push, a
|
|
38
|
+
credentialed command); a command that names no secret never. A row replaces the one before it and is never dropped: a
|
|
39
|
+
restored child that is done or stopped never starts, so no restore reads a finished Effect's row, and the settled and
|
|
40
|
+
aborted rows keep the Effect's id, kind and outcome for the run's life: the record of what the run's Effects did. A
|
|
41
|
+
settled row's output TAIL is dropped once the save that carries its transition is on disk — that save's snapshot no
|
|
42
|
+
longer holds the Effect running, so no restore reads it, and the full stream is on the run's feed. A push row keeps its
|
|
43
|
+
Repo Slot, its published commit, its source, its `squashed` and its sync's answer whole: every later push of its
|
|
44
|
+
Workspace and Repo Slot sends them as one of its pairs, and the newest push's sync, when it did not finish, runs again
|
|
45
|
+
(ADR-0067); git's words are its tail, and so is its bundle, except on the newest push of its Repo Slot while that push's
|
|
46
|
+
sync is `busy` or `not-synced`: that row keeps the bundle, which the sync that runs again moves the branch from. Only
|
|
47
|
+
the newest push's sync runs again, so once a newer push of the slot published, the older row's bundle goes at the next
|
|
48
|
+
save — at most one bundle per Repo Slot. The ledger grows by one short row per Effect a run starts, never by its tails,
|
|
49
|
+
and lives as long as the run's row (ADR-0065). An outstanding pick's hold is saved in the same blob and keeps its
|
|
50
|
+
`chain` whole until the pick is answered (ADR-0067); that is the only place an Effect's tails accumulate.
|
|
51
|
+
|
|
52
|
+
**Pending timers** do not ride the snapshot either. The host persists a **timer table** beside it in the same save: one
|
|
53
|
+
row per pending `after`, named delay, delayed `raise` or delayed `sendTo` in the run's tree, with its absolute deadline.
|
|
54
|
+
The author writes plain xstate `after`; there is no kit timer actor, and `after` is refused nowhere.
|
|
55
|
+
|
|
56
|
+
## Mechanism (the non-obvious parts)
|
|
57
|
+
|
|
58
|
+
- **Re-attach is driven by the child's persisted input, not the parent `invoke`.** On restore, xstate v5 re-spawns an
|
|
59
|
+
invoked actor from its **persisted input** — it does **not** re-evaluate the parent's `invoke: { input: … }`. So the
|
|
60
|
+
re-attach lever is rewriting the child's persisted input on hydrate (drop `prompt`, mark it attaching) so the run
|
|
61
|
+
resumes its stream instead of re-POSTing. Keep the parent-side input expression as defence-in-depth, but know it is
|
|
62
|
+
not what fires on restore.
|
|
63
|
+
- **A timer is re-armed by the host, because xstate 5 drops it.** xstate 5 keeps a delayed event on the actor SYSTEM's
|
|
64
|
+
scheduler, not in the snapshot, and a restore runs no entry action again, so a pending `after` never fires after a
|
|
65
|
+
restore (Pool's `pollEvery` included, ADR-0017). At each save the host reads the scheduler's table and writes each
|
|
66
|
+
timer as `{ source, target, id, event, delay, deadline }`, actors named by their child-id path below the root. The
|
|
67
|
+
deadline is a time on the host's wall clock (`RunHostOptions.now`, `Date.now` by default; tests pass one that does not
|
|
68
|
+
step). On restore the order is: `start()`, then re-arm each row with `deadline − now` clamped to `[0, delay]`, in
|
|
69
|
+
deadline order. Re-armed before `start()`, a timer is armed twice, because the system's own `start()` schedules its
|
|
70
|
+
table. The id is xstate's own, so a state's exit after the re-arm cancels a re-armed timer. `start()` drains the
|
|
71
|
+
mailbox, so a child that sends on start can exit a state before any row is re-armed; the host records each cancel
|
|
72
|
+
during `start()` and does not re-arm a row it names. A child stopped during `start()` is stopped with xstate's
|
|
73
|
+
`cancelAll`, which sees an empty table, and its parent state entered again puts a new child at the same path; so the
|
|
74
|
+
host resolves each row's actors before `start()` and re-arms a row only on the same actors after it. So a timer fires
|
|
75
|
+
once, after the time that remained; at once if its deadline passed while the Orchestrator was down, and timers that
|
|
76
|
+
are all overdue fire in the order they fell due; never if an exit cancelled it, before the save, during `start()` or
|
|
77
|
+
after the re-arm; and never later than its own delay when the wall clock went backwards. A wall clock that jumped
|
|
78
|
+
forwards fires it early: only the wall clock survives a process. "Once" is with respect to the saved state: a timer
|
|
79
|
+
that fired after the last save fires again on restore, as any event after the last save replays. A delayed send to an
|
|
80
|
+
actor outside the run's tree is not recorded; jr2 has no such idiom.
|
|
81
|
+
- **The re-arm reads an xstate internal** (`system.getSnapshot()._scheduledEvents`, `system.scheduler.schedule`, and
|
|
82
|
+
`system.scheduler.cancel`, wrapped for the length of `start()`; `packages/orchestrator/src/timers.ts`). The same two
|
|
83
|
+
scheduler functions are also wrapped for the life of the run's system, to tell a timer that fires from an event an
|
|
84
|
+
actor sends at once: both reach the inspection stream alike, and the scheduler forgets a timer as it fires it. An
|
|
85
|
+
outstanding pick's stopped answer needs that difference (ADR-0024). Raw-xstate tests that show xstate itself dropping
|
|
86
|
+
the timer are the canary for an xstate bump. xstate 6 persists timers in the snapshot and re-arms them with the same
|
|
87
|
+
deadline and clamp; on xstate 6 the timer table is deleted.
|
|
88
|
+
- **Reconcile against the live world before re-attaching.** Restore is not blind resume: a present Sandbox CR →
|
|
89
|
+
re-attach; an absent one → the defined failure path (`workspace.lost` into the body — ADR-0012), never a silent
|
|
90
|
+
re-provision.
|
|
91
|
+
- **Reconcile against the MACHINE too** (added 2026-08-02,
|
|
92
|
+
[ADR-0030](0030-a-snapshot-names-the-machine-it-was-written-under.md)). "Reconstructable from a persisted snapshot"
|
|
93
|
+
was only ever true of the Machine that WROTE it, and a run resolves its workflow by filename while the state volume
|
|
94
|
+
outlives the image — so a run parked across a deploy meets whatever was baked in next. The snapshot now carries a
|
|
95
|
+
digest of its Machine's shape, and a mismatch is refused rather than interpreted. This is not belt-and-braces: xstate
|
|
96
|
+
accepts a `value` naming a state the chart no longer has and starts with `value: undefined`, so nothing downstream
|
|
97
|
+
would have caught it.
|
|
98
|
+
|
|
99
|
+
## Consequences
|
|
100
|
+
|
|
101
|
+
- Actors may not stash live connections in context for convenience; anything live is rebuilt per-invocation from
|
|
102
|
+
serializable input (the Harness client from an `endpoint`, the kube client from ambient config) — which is also why a
|
|
103
|
+
mock↔real swap happens at the wire (ADR-0011's stub Harness), never inside actor code.
|
|
104
|
+
- An Effect has no at-most-once sliver: its intent row is on disk before the call leaves, so a restart either finds no
|
|
105
|
+
row (nothing was sent) or finds the intent and settles `unknown` when it cannot re-attach — or, for an http call the
|
|
106
|
+
author marked `idempotent` that no replica holds, sends it again under the same id.
|
|
107
|
+
- The known durability edge stands for Agents: a crash between "run admitted" and "admission persisted" degrades to an
|
|
108
|
+
at-most-once re-POST (a live-run probe replays from `-1`; a genuinely-absent run re-POSTs), and an in-flight POST at
|
|
109
|
+
the instant of the kill is the irreducible at-most-once sliver.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# jr2 is a library + CLI; an Orchestrator instance hosts many workflows
|
|
2
|
+
|
|
3
|
+
jr2 ships as a **library** (engine + agent-protocol) and a **CLI** (`jr2`). You don't build a workflow into its own
|
|
4
|
+
image. Instead you `jr2 init` an **Orchestrator instance** — a small, user-owned _code_ project (a folder) that imports
|
|
5
|
+
the kit, configures it, and holds one or more **workflows** (Machines authored via `jr2Setup`, ADR-0015). One instance
|
|
6
|
+
hosts **many** workflows; the CLI publishes that instance's workflow modules to a running Orchestrator as its Assembly
|
|
7
|
+
([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)). The instance folder is the
|
|
8
|
+
**jr2 Application** — the GitOps unit; the same folder/manifests run on kind locally and on a real cluster.
|
|
9
|
+
|
|
10
|
+
Machine definitions are **code, not declarative config.** We rejected expressing machines as YAML/CRD config: a useful
|
|
11
|
+
agentic Machine is mostly logic (guards, prompt-building, `assign`s), and a declarative form becomes a DSL-over-xstate
|
|
12
|
+
that loses type-safety and still cannot express branching. The instance is a code project (it imports `@jr2/*`), so
|
|
13
|
+
"code, not config" survives — machine definitions simply live in the user's instance folder rather than baked into the
|
|
14
|
+
kit. The rule has one deliberate complement: a human writes code, and what a RUN supplies is data — a Chart bound into a
|
|
15
|
+
Machine's Open body ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)) carries no logic of its own.
|
|
16
|
+
|
|
17
|
+
## The Orchestrator instance
|
|
18
|
+
|
|
19
|
+
An instance is a **stateful, single-writer daemon**: it holds the live in-memory xstate actors, so it runs `replicas: 1`
|
|
20
|
+
and persists its snapshots to sqlite on its volume (ADR-0009, ADR-0065). `replicas: 1` means single _writer_ (no
|
|
21
|
+
split-brain on the snapshot) — **not** one workflow per process. One instance registers N workflows and **hosts many
|
|
22
|
+
runs** of any of them. Work arrives two ways that both reduce to injecting events into an actor: a **Source** (pull,
|
|
23
|
+
ADR-0017) and the **HTTP API** (push) — so push/pull coexist with no special machinery. Isolation or independent scaling
|
|
24
|
+
is achieved by running **another instance**, not by replicating one run; the default is one instance, many workflows.
|
|
25
|
+
|
|
26
|
+
## The Harness stays on the Agent side
|
|
27
|
+
|
|
28
|
+
The Agent/Sandbox Harness is jr2's own server, `@jr2/harness` (ADR-0002/0018/0027) — and the Orchestrator is **not** a
|
|
29
|
+
harness: its state is an xstate snapshot, not an agent session, and its loop is a deterministic state machine, not an
|
|
30
|
+
LLM-in-harness. The two share ergonomics, not implementation: the instance folder convention (`jr2.config.ts` + the
|
|
31
|
+
discovered `workflows/` dir — the Agents ride the Machines it holds, ADR-0049), the `jr2` CLI as the primary interface,
|
|
32
|
+
and an HTTP API shaped as "durable run addressed by id" (`POST` to start/feed, `GET /…/:id` for status, SSE for events)
|
|
33
|
+
— so both sides of the system speak one protocol without sharing a runtime.
|
|
34
|
+
|
|
35
|
+
## The Orchestrator pod
|
|
36
|
+
|
|
37
|
+
It is sized and prioritised like the other control pods (ADR-0060: requests 500m and 1Gi, memory limit 1Gi, CPU
|
|
38
|
+
unlimited, the `jr2-control` PriorityClass), keeps its store on its own volume (ADR-0065), and carries two probes on
|
|
39
|
+
`/healthz`. Readiness (2s period, 15 failures) takes a stalled process out of its Service after 30s and does nothing
|
|
40
|
+
else: a `replicas: 1` writer that is restarted is an outage plus a restore, so a GC pause or a busy node must never
|
|
41
|
+
trigger one. Liveness (10s period, 30 failures) restarts a process that has answered nothing for five minutes: a process
|
|
42
|
+
silent that long is not stalled but dead, and without it the pod would sit NotReady until a human deleted it, while a
|
|
43
|
+
process that exits is restarted by the container's restart policy regardless.
|
|
44
|
+
|
|
45
|
+
## Consequences
|
|
46
|
+
|
|
47
|
+
- The deployable unit is the Orchestrator **Kit image** plus the instance's **Assembly**, which `jr2 up` publishes to
|
|
48
|
+
the running Orchestrator ([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md);
|
|
49
|
+
a CI-only `jr2 build` is planned), run as one `replicas: 1` `Deployment` + `Service`. The two-repo pattern (instance
|
|
50
|
+
code → Assembly; manifests → GitOps) is expected, and the instance folder carries both.
|
|
51
|
+
- Concrete workflows do **not** live in the jr2 source repo — only the kit, the Machines it ships (`@jr2/machines`,
|
|
52
|
+
ADR-0054), and the `templates/default` model instance do. A user's workflows live in their `jr2 init` instance folder.
|
|
53
|
+
- jr2 ships and deploys only its **`Sandbox` operator** (its own CRD), deployed once per cluster. The snapshot store is
|
|
54
|
+
sqlite on the Instance's own volume (ADR-0065); the model backend is a **dependency you provide** — jr2 points at it
|
|
55
|
+
(model env), it does not own or operate it. Instances are deployed per user/project.
|
|
56
|
+
- Users interact through the **`jr2` CLI** far more than raw HTTP; the HTTP API is the machine-to-machine surface (push
|
|
57
|
+
work, human-in-the-loop, status) the CLI itself sits on top of.
|
|
58
|
+
- Code is never loaded dynamically: only `jr2 up` publishes an Assembly, typechecked on the host first
|
|
59
|
+
([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)). A run may bind a
|
|
60
|
+
Machine's Open body with a Chart, which is data ([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)).
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# The `jr2` CLI and instance interface
|
|
2
|
+
|
|
3
|
+
ADR-0008 fixed that jr2 is a library + CLI and that an Orchestrator instance is a user-owned folder hosting many
|
|
4
|
+
workflows. This ADR fixes the **instance folder convention**, the **orchestrator HTTP API**, and the **`jr2` CLI** — one
|
|
5
|
+
set of conventions both sides of the system speak. Operational mechanics (how `jr2 up` converges a cluster, addressing,
|
|
6
|
+
secrets) live in ADR-0019.
|
|
7
|
+
|
|
8
|
+
## Instance folder convention (filename discovery)
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
my-orchestrator/
|
|
12
|
+
jr2.config.ts # instance config: reach and credentials — harness, git.credentials, registry; nothing a Machine names (ADR-0050)
|
|
13
|
+
workflows/ # filename-discovered: workflows/review.ts (contract: export const machine) → "review"
|
|
14
|
+
images/default/ # the fallback docker context a `workspace()` with no `image` uses (ADR-0037); a path convention, not discovery
|
|
15
|
+
manifests/ # user-supplied objects applied by `jr2 up` (e.g. SealedSecrets); optional
|
|
16
|
+
.env # local secrets + deployment-varying env; uncommitted
|
|
17
|
+
.jr2/ # scratch; nothing durable lives on the host (state is in-cluster, ADR-0019)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`workflows/<name>.ts` registers a workflow named `<name>` via `export const machine` — the one-export module contract
|
|
21
|
+
(ADR-0015; vocabulary rides the machine object, so there is no manifest export). Workflows are the one discovered kind
|
|
22
|
+
because nothing in code names one: `jr2 run` and the API do. Agents, images, and Repos are named from Machines, so they
|
|
23
|
+
ride the Machine — Agent slots, `workspace()` options, Repo Slots (ADR-0049, ADR-0051) — and nothing in config names
|
|
24
|
+
them (ADR-0050).
|
|
25
|
+
|
|
26
|
+
**The instance repo is a deployment assembly, not a sharing unit** (ADR-0019): reusable workflows/agents are published
|
|
27
|
+
as npm packages and re-exported here; `jr2.config.ts` holds only what is specific to this deployment's reach,
|
|
28
|
+
credentials, and cluster — committed, because the instance repo is the GitOps unit (ADR-0008), with deployment-varying
|
|
29
|
+
values resolved from env.
|
|
30
|
+
|
|
31
|
+
## Minimum config, and the Repo cache
|
|
32
|
+
|
|
33
|
+
`jr2.config.ts` favors convention over configuration — an Instance whose Machines compose Sandboxes can be as small as:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { defineConfig } from "@jr2/orchestrator";
|
|
37
|
+
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
name: "my-orchestrator",
|
|
40
|
+
git: { credentials: [{ match: "*", token: "JR2_GIT_TOKEN", sshKey: "jr2-git-ssh" }] }, // narrow before untrusted runs
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- **`name` is the instance's identity**; its namespace defaults to it (`-n` overrides — ADR-0019).
|
|
45
|
+
- **There is no `repos` list.** A Repo is a slot on a `workspace()`, bound by url (ADR-0051); `jr2.config.ts` holds what
|
|
46
|
+
reaches one — `git.credentials`, the prefix-matched list that is also the fence a per-run slot's url must pass. The
|
|
47
|
+
scaffold writes one wildcard entry, commented, for the user to narrow (ADR-0051). The operator keeps a bare cache per
|
|
48
|
+
node from the `Repo` resources the Orchestrator creates off its Machines (ADR-0004); Sandboxes clone `--shared`
|
|
49
|
+
against it. There is no host-side directory — a Repo pods should see must be fetchable from the cluster.
|
|
50
|
+
- **A registered Machine that composes a Sandbox is the data-plane switch** (ADR-0051), read off `jr2 up`'s walk: with
|
|
51
|
+
one, the instance gets the kubectl Sandbox backend; without one, the instance is workspace-less. There is **no
|
|
52
|
+
`images` config block**: `jr2 up` builds every image it deploys and resolves each to a content-addressed tag
|
|
53
|
+
([ADR-0038](0038-jr2-up-builds-every-image-it-deploys.md)). Image composition is per Workspace, not per instance — a
|
|
54
|
+
`workspace()` names its Sandbox Image statically (a `file:` docker context the Machine ships or a registry ref,
|
|
55
|
+
[ADR-0037](0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md),
|
|
56
|
+
[ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md)) and opts into a User Container the same way
|
|
57
|
+
([ADR-0005](0005-sandbox-pod-composition.md)); `images/default` is the one path convention `jr2 up` still checks.
|
|
58
|
+
Every Harness pod gets a Custodian — an Agent without one cannot act (ADR-0013, ADR-0059) — and that is not
|
|
59
|
+
configurable. Agent-runtime concerns live in `harness` (ADR-0018).
|
|
60
|
+
- **`harness` is the agent-runtime section** (ADR-0018): custom provider (`api`, `baseUrl`), the gateways pi's catalog
|
|
61
|
+
providers call (`catalog`) and any other env — never which model to use; each definition names its own (ADR-0018).
|
|
62
|
+
- **`secrets` is the Instance's one list of secrets**
|
|
63
|
+
([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)): the credentials the Agents use — an
|
|
64
|
+
Anthropic key, read from `process.env`/`.env`, held by the Custodian and never by the Harness (ADR-0059) — and the
|
|
65
|
+
ones an Effect names, which only its Effector holds. Where each goes is read, never declared.
|
|
66
|
+
- **`registry`** (deployment-varying, resolve from env): absent → images are `kind load`-ed; present → pushed
|
|
67
|
+
(ADR-0019). **`kitRegistry`** (also env) re-homes the published Kit image refs for self-hosted, air-gapped, or
|
|
68
|
+
mirror-only clusters ([ADR-0044](0044-kit-images-live-at-a-canonical-home-a-self-host-mirrors-it.md)).
|
|
69
|
+
- **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.
|
|
72
|
+
- Git credentials for private repos: an HTTPS token from `.env`, or a `jr2-git-ssh` Secret holding the key `jr2 up`
|
|
73
|
+
asked the user to choose — a generated in-cluster deploy keypair (the recommended default), a local key, or one pasted
|
|
74
|
+
on stdin (ADR-0019, [ADR-0047](0047-the-git-ssh-key-source-is-the-users-choice.md)). Kube target: the current
|
|
75
|
+
`kubectl` context (`--context` to override).
|
|
76
|
+
|
|
77
|
+
## Machines ship as npm; instances compose or customize
|
|
78
|
+
|
|
79
|
+
The kit publishes to npm under `@jr2/*`; stock Agent definitions ship in **`@jr2/agents`** (coder, reviewer, …), and a
|
|
80
|
+
package may ship whole Machines with their Agents and image inside (ADR-0049). Because a definition is plain data
|
|
81
|
+
(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).
|
|
84
|
+
|
|
85
|
+
## Orchestrator HTTP API (hono, run-addressed-by-id)
|
|
86
|
+
|
|
87
|
+
Push + control + observe only — the **pull** path needs no HTTP (the Orchestrator's Source pulls, ADR-0017). Auth bands
|
|
88
|
+
per ADR-0014: structure + observation open; the Agent surface takes the Sandbox token; run state, control, and gates
|
|
89
|
+
take the Instance token.
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
GET /workflows # list registered workflows [open]
|
|
93
|
+
GET /workflows/:name/machine /viz/* # machine structure + visualizer [open]
|
|
94
|
+
GET /workflows/:name/runs[, /:runId/events] # observation projection (ADR-0014) [open]
|
|
95
|
+
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]
|
|
102
|
+
GET /repos # per-Repo node-cache sync state (ADR-0048) [instance]
|
|
103
|
+
GET /agents/:iid/surface POST /agents/:iid/events # the Menu, via the Custodian [sandbox]
|
|
104
|
+
POST /sandboxes/:name/fetch # the ask (ADR-0053) [sandbox]
|
|
105
|
+
GET /healthz GET /readyz
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`GET /repos` is [ADR-0048](0048-the-orchestrator-boots-without-its-repos.md)'s observability half: a failed clone
|
|
109
|
+
degrades the Repo, not the boot, so each `Repo` resource's status (per node: synced, or git's own error) is a thing to
|
|
110
|
+
ask the instance for, read off the resources (ADR-0051). `jr2 status` with **no** run id renders it — the place
|
|
111
|
+
ADR-0047's "register the key, the cache agent retries" points at. Instance band, not open: a row names a Repo and
|
|
112
|
+
carries git's error text, exactly the class of author/deployment detail the open projections strip (ADR-0014).
|
|
113
|
+
|
|
114
|
+
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)).
|
|
120
|
+
|
|
121
|
+
## The `jr2` CLI
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
# lifecycle (instance)
|
|
125
|
+
jr2 init [dir] [--name <n>] # scaffold the minimum runnable instance
|
|
126
|
+
jr2 check # the typecheck and the Machine walk of `up`, with no cluster (ADR-0050)
|
|
127
|
+
jr2 up [--yes] [--force] [--skip-provider-preflight]
|
|
128
|
+
# converge the current context to this instance (ADR-0019)
|
|
129
|
+
jr2 down [--all] [--yes] # remove the instance from the cluster (--all: operator too)
|
|
130
|
+
jr2 gc [--dry-run] # sweep unreachable labeled images — the same sweep up/down run (ADR-0039)
|
|
131
|
+
|
|
132
|
+
# kit (instance-less)
|
|
133
|
+
jr2 kit push <registry> # mirror the published Kit images into a self-hosted registry (ADR-0044)
|
|
134
|
+
|
|
135
|
+
# what runs where
|
|
136
|
+
jr2 version [--local] [--json] # this jr2, the global that handed off, the Instance's Kit, and what the cluster runs
|
|
137
|
+
|
|
138
|
+
# runs / workflows (wrap the HTTP API)
|
|
139
|
+
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)
|
|
141
|
+
jr2 send <runId|abbrev> --event CANCEL
|
|
142
|
+
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>
|
|
148
|
+
```
|
|
149
|
+
|
|
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.
|
|
155
|
+
|
|
156
|
+
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.
|
|
159
|
+
|
|
160
|
+
A planned `jr2 build` (build + push + render manifests, no apply — the pure-GitOps CI verb) is deferred; `jr2 up` in CI
|
|
161
|
+
covers the interim (ADR-0019).
|
|
162
|
+
|
|
163
|
+
## Settled CLI behavior (v1)
|
|
164
|
+
|
|
165
|
+
**Instance addressing.** The CLI finds its instance by walking up from cwd to the directory containing `jr2.config.ts` —
|
|
166
|
+
the root marker. The _deployment_ is addressed by the current kube context + the instance's namespace: run-verbs
|
|
167
|
+
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).
|
|
171
|
+
|
|
172
|
+
**The command table.** Each verb is an entry in `packages/cli/src/command-table.ts`: its arguments, its flags, the env
|
|
173
|
+
vars it reads, what it prints, and its ADRs, as data. The verb's `parseArgs` options come from its entry, and so does
|
|
174
|
+
the help, so a flag cannot be accepted and not documented, or documented and not read. `jr2 --help` prints the whole
|
|
175
|
+
table. `jr2 <verb> --help` or `-h` prints that verb's entry, runs nothing, and exits 0. A usage error prints the verb's
|
|
176
|
+
usage line. The CLI Reference of the docs is generated from the same table
|
|
177
|
+
([ADR-0072](0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md)).
|
|
178
|
+
|
|
179
|
+
**Two output classes (amended 2026-09-20).** The stream rule below — JSON result on stdout, activity on stderr — was
|
|
180
|
+
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.
|
|
187
|
+
|
|
188
|
+
**`jr2 version` (amended 2026-09-20).** The report you paste into a bug: what is _here_ and what is _deployed_, side by
|
|
189
|
+
side, so the gap between the halves is the answer ("you edited the pin and never ran `jr2 up`"). The local half: `cli`
|
|
190
|
+
(the copy that runs — version + real path, so the path says global or Instance), `global` (the copy that handed off,
|
|
191
|
+
ADR-0056), `kit` (the `@jr2/orchestrator` the Instance resolves + the ADR-0056 check result, and
|
|
192
|
+
`package.json pins X, reinstall` when the manifest disagrees with what is installed), `instance` (name, root, and mode:
|
|
193
|
+
kit checkout or installed from which registry — the reader needs the mode to know whether deployed Kit tags are versions
|
|
194
|
+
or content hashes), `node`. The deployed half, best-effort: `orchestrator` (what the pod answers on `/healthz` —
|
|
195
|
+
version + hash, flagging a Deployment label that disagrees as an incomplete rollout), `operator` (its version label —
|
|
196
|
+
never downgraded, so no skew verdict on it), `harness`/`custodian` and any Sandbox Image refs from the `jr2-images`
|
|
197
|
+
ConfigMap pods actually read, and one `skew` verdict comparing the two kit numbers: `same`/`behind`/`ahead`/`unknown`,
|
|
198
|
+
with the `jr2 up` consequence spelled out and, in checkout mode, a reminder that `same` is weak and the hash is the
|
|
199
|
+
address. Nothing is computed that `jr2 up` computes: no bundle staging, no local hash. It is the one Instance verb that
|
|
200
|
+
reads the Instance without asserting it — a Kit mismatch is a line, never the ADR-0056 refusal, because this is the verb
|
|
201
|
+
you reach for when that refusal fires. It hands off like every verb. The shared addressing flags (`--url`, `-n`,
|
|
202
|
+
`--context`) as every run verb; `--url` leaves only the orchestrator line; `--local` skips the cluster. Exit 0 whatever
|
|
203
|
+
it finds: a report. No registry lookup: "out of date" here means behind the Instance, and `npm view` answers the other
|
|
204
|
+
question.
|
|
205
|
+
|
|
206
|
+
**`jr2 check`.** It runs the Instance check of `jr2 up` and nothing more. First it runs the Instance's own
|
|
207
|
+
`tsc --noEmit`. Then it loads each registered Workflow and walks its Machine. The load and the walk find the refusals
|
|
208
|
+
that the compiler cannot make: a refusal of `createMachine`, `defineEvent` or `workspace()` when a module loads, an Open
|
|
209
|
+
part, a Chart file, a dispatch, an Effect secret, a Size. It reads no kube context. It touches no cluster, docker or
|
|
210
|
+
registry, and it writes no file. When it refuses, it exits 1 with the `refusing:` or `error:` line of `jr2 up`. It is
|
|
211
|
+
one function with `jr2 up` (`checkInstance`), not a copy, so the two cannot disagree.
|
|
212
|
+
|
|
213
|
+
It is for an Assistant that may not run `jr2 up`. `npm run typecheck` loads no module. And `node` cannot import a
|
|
214
|
+
workflow module of an installed Instance. The kit's `.ts` sources are under `node_modules`, and Node does not strip
|
|
215
|
+
types there. The `jr2` binary strips them. The scaffold names `jr2 check` as its `check` script. The AGENTS.md block
|
|
216
|
+
names it as the step before the Assistant gives a change to the user (ADR-0072).
|
|
217
|
+
|
|
218
|
+
It does not stage the Assembly. Staging installs the production dependencies from the lockfile, and its refusals are
|
|
219
|
+
about that install. It also does not write the AGENTS.md block. An Assistant or CI must be able to run a check with no
|
|
220
|
+
diff as a result.
|
|
221
|
+
|
|
222
|
+
**`jr2 run` — blocking, attach-by-default.** Start the run, stream activity to **stderr**, print the terminal
|
|
223
|
+
`RunStatus` as JSON to **stdout**, exit — so `jr2 run … | jq` yields just the result. It **attaches to the running
|
|
224
|
+
orchestrator**, not a temporary per-invocation runtime — a jr2 run is durable and may park on a Gate indefinitely,
|
|
225
|
+
outliving the CLI call. `--detach` starts the run, prints its `runId`, returns.
|
|
226
|
+
|
|
227
|
+
**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({…})`).
|
|
231
|
+
|
|
232
|
+
**Terminal runs stay readable.** The final snapshot persists to the store before the run drops from the live registry,
|
|
233
|
+
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.)
|
|
235
|
+
|
|
236
|
+
**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
|
+
form: `jr2 status 1a2b3c4d`. **Prefix only** — not fuzzy, not suffix — which keeps it an indexed range scan and keeps
|
|
238
|
+
the mental model borrowed intact. Four characters is the floor, and that floor is about **noise, not safety**: a short
|
|
239
|
+
prefix never resolves to the wrong run, it resolves to a list. Safety comes from ambiguity being an error — matching
|
|
240
|
+
more than one id prints the candidates and fails (exit 1), never guesses. A malformed or too-short argument is a usage
|
|
241
|
+
failure (exit 2); a full id short-circuits resolution entirely, so scripted pipelines issue unchanged traffic.
|
|
242
|
+
|
|
243
|
+
**The CLI resolves; the addressed routes stay full-id.** Abbreviation is a human affordance and lives on the human
|
|
244
|
+
surface — `GET /runs/resolve` answers prefix→ids, and the CLI then addresses the run by its full id. `/runs/:runId` and
|
|
245
|
+
both event POSTs never accept a prefix, because **a prefix is not an identity**: one that resolves today goes ambiguous
|
|
246
|
+
tomorrow when an unrelated run starts, and `jr2 send <prefix> --event CANCEL` is a write. Resolving as a separate
|
|
247
|
+
read-only step is what guarantees no write is ever prefix-sensitive. (It also gives a bad id a real error: `host.stop()`
|
|
248
|
+
returns silently for an unknown run, so an unresolved CANCEL used to report success.)
|
|
249
|
+
|
|
250
|
+
The candidate set is the ids that **exist**, not the ones that read: `lost` runs are included, because dropping them
|
|
251
|
+
would let a prefix they share with a live run resolve silently to the live one. The set unions the live registry with
|
|
252
|
+
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.
|
|
257
|
+
|
|
258
|
+
**`jr2 init` (v1).** Scaffolds the minimum runnable instance: `jr2.config.ts` (root marker), `package.json` (deps on
|
|
259
|
+
`@jr2/*` + xstate), one starter `workflows/<name>.ts`, `images/default/Dockerfile` (the Sandbox Image every Workspace
|
|
260
|
+
falls back to, ADR-0037), `.gitignore` (`.jr2/`, `.env`, `node_modules/`), and `AGENTS.md` with a `CLAUDE.md` that
|
|
261
|
+
imports it (the index of the Kit's bundled docs for an Assistant, ADR-0072). `[dir]` positional (default cwd); `--name`
|
|
262
|
+
for the package name (default: the folder's name). It skips each file that exists, and says so; for an `AGENTS.md` with
|
|
263
|
+
no markers, or a `CLAUDE.md` that does not import `AGENTS.md`, it also says what to add. `manifests/` and `.env` are
|
|
264
|
+
added by their later slices. No auto-install — it prints the next step, naming no package manager, since the instance's
|
|
265
|
+
lockfile is what picks one ([ADR-0043](0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md)).
|
|
266
|
+
|
|
267
|
+
## Consequences
|
|
268
|
+
|
|
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
|
|
271
|
+
([ADR-0043](0043-the-kit-is-tested-as-installed-a-local-registry-stands-in-for-npm.md)). The CLI ships as the `jr2`
|
|
272
|
+
bin (`npx jr2`).
|
|
273
|
+
- Workspaces are a first-class CLI resource backed by the operator's `Sandbox` CRs, label-linked to their runs.
|
|
274
|
+
- Code is never loaded from a third party at run time (ADR-0008, ADR-0069): discovery is over the instance's own
|
|
275
|
+
Assembly, which only `jr2 up` publishes. A run may bind a Machine's Open body with a Chart, which is data (ADR-0070).
|