@jr2/orchestrator 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console/components/app.ts +55 -15
- package/console/components/bottom-drawer.ts +259 -0
- package/console/components/drawer.ts +14 -6
- package/console/components/fleet.ts +36 -13
- package/console/components/machine-pane.ts +61 -5
- package/console/components/nav.ts +54 -8
- package/console/conversation.ts +161 -0
- package/console/login.ts +123 -0
- package/console/main.ts +600 -60
- package/console/store.ts +611 -64
- package/console/style.css +231 -0
- package/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +142 -29
- package/docs/concepts/architecture.md +60 -34
- package/docs/concepts/callers-and-hooks.md +205 -0
- package/docs/concepts/charts.md +13 -8
- package/docs/concepts/composition.md +10 -1
- package/docs/concepts/converge.md +7 -2
- package/docs/concepts/durable-runs.md +43 -16
- package/docs/concepts/effects.md +133 -50
- package/docs/concepts/gates.md +95 -25
- package/docs/concepts/machines-and-workflows.md +14 -4
- package/docs/concepts/pools-and-sources.md +193 -17
- package/docs/concepts/secrets-and-trust.md +186 -62
- package/docs/concepts/the-menu.md +14 -0
- package/docs/concepts/workspaces.md +135 -56
- package/docs/get-started/a-model-decides.md +14 -13
- package/docs/get-started/an-agent-in-a-sandbox.md +46 -35
- package/docs/get-started/connect-a-model.md +1 -1
- package/docs/get-started/create-an-instance.md +17 -14
- package/docs/get-started/have-your-assistant-write-a-machine.md +111 -40
- package/docs/get-started/install.md +4 -0
- package/docs/guides/add-tools-to-the-sandbox.md +72 -15
- package/docs/guides/answer-a-gate-from-a-forge.md +304 -0
- package/docs/guides/answer-a-gate.md +74 -37
- package/docs/guides/bind-open-parts.md +29 -12
- package/docs/guides/call-an-mcp-tool-from-a-machine.md +572 -0
- package/docs/guides/cancel-a-run.md +26 -16
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +19 -7
- package/docs/guides/give-a-caller-a-scope.md +203 -0
- package/docs/guides/give-an-agent-mcp-tools.md +422 -0
- package/docs/guides/iterate-on-a-machine-on-kind.md +10 -9
- package/docs/guides/let-an-agent-write-a-chart.md +14 -7
- package/docs/guides/look-inside-a-sandbox.md +115 -42
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/open-the-console.md +63 -14
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/read-a-conversation.md +225 -0
- package/docs/guides/run-a-chart-by-name.md +17 -16
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +34 -27
- package/docs/guides/set-an-agents-access-and-dials.md +69 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/start-a-run-for-each-alert.md +290 -0
- package/docs/guides/start-a-run-from-a-run.md +18 -16
- package/docs/guides/start-a-run.md +24 -18
- package/docs/guides/steer-an-agent.md +332 -0
- package/docs/guides/use-a-hosted-provider.md +7 -1
- package/docs/guides/watch-a-run.md +146 -117
- package/docs/guides/write-a-hook-kind.md +238 -0
- package/docs/guides/write-a-tool-as-an-mcp-server.md +351 -0
- package/docs/guides/write-a-workflow.md +18 -8
- package/docs/llms.txt +110 -23
- package/docs/reference/authoring-surface/agent.md +35 -12
- package/docs/reference/authoring-surface/answer.md +20 -4
- package/docs/reference/authoring-surface/arg.md +28 -6
- package/docs/reference/authoring-surface/basic.md +1 -1
- package/docs/reference/authoring-surface/bearer.md +1 -1
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/carry.md +23 -0
- package/docs/reference/authoring-surface/chartschema.md +1 -1
- package/docs/reference/authoring-surface/chartschemafor.md +1 -1
- package/docs/reference/authoring-surface/command.md +37 -22
- package/docs/reference/authoring-surface/customize.md +4 -1
- package/docs/reference/authoring-surface/defineconfig.md +1 -1
- package/docs/reference/authoring-surface/defineevent.md +1 -1
- package/docs/reference/authoring-surface/dispatch.md +5 -4
- package/docs/reference/authoring-surface/fail.md +18 -5
- package/docs/reference/authoring-surface/file.md +37 -17
- package/docs/reference/authoring-surface/frompick.md +1 -1
- package/docs/reference/authoring-surface/gate.md +118 -0
- package/docs/reference/authoring-surface/group-agents.md +15 -0
- package/docs/reference/authoring-surface/group-charts.md +16 -0
- package/docs/reference/authoring-surface/group-composition.md +25 -0
- package/docs/reference/authoring-surface/group-config.md +17 -0
- package/docs/reference/authoring-surface/group-effects.md +48 -0
- package/docs/reference/authoring-surface/group-failure.md +13 -0
- package/docs/reference/authoring-surface/group-gates.md +16 -0
- package/docs/reference/authoring-surface/group-machine-and-events.md +15 -0
- package/docs/reference/authoring-surface/group-steering.md +12 -0
- package/docs/reference/authoring-surface/group-types-these-signatures-name.md +137 -0
- package/docs/reference/authoring-surface/group-workspaces-and-repos.md +15 -0
- package/docs/reference/authoring-surface/http.md +109 -9
- package/docs/reference/authoring-surface/isfailure.md +1 -1
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +14 -4
- package/docs/reference/authoring-surface/mcp.md +107 -0
- package/docs/reference/authoring-surface/open.md +41 -8
- package/docs/reference/authoring-surface/openbody.md +1 -1
- package/docs/reference/authoring-surface/paramsof.md +23 -5
- package/docs/reference/authoring-surface/pool.md +79 -11
- package/docs/reference/authoring-surface/push.md +31 -13
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/reread.md +19 -0
- package/docs/reference/authoring-surface/secret.md +26 -5
- package/docs/reference/authoring-surface/sentby.md +1 -1
- package/docs/reference/authoring-surface/skip.md +22 -0
- package/docs/reference/authoring-surface/source.md +59 -4
- package/docs/reference/authoring-surface/steer.md +93 -0
- package/docs/reference/authoring-surface/text.md +1 -1
- package/docs/reference/authoring-surface/type-agentdefinition.md +16 -3
- package/docs/reference/authoring-surface/type-agentmcpserver.md +54 -0
- package/docs/reference/authoring-surface/type-agentslots.md +4 -1
- package/docs/reference/authoring-surface/type-anyframepath.md +14 -0
- package/docs/reference/authoring-surface/type-anyworktreepath.md +15 -0
- package/docs/reference/authoring-surface/type-bindactors.md +14 -0
- package/docs/reference/authoring-surface/type-binding.md +6 -7
- package/docs/reference/authoring-surface/type-caller.md +40 -0
- package/docs/reference/authoring-surface/type-callerrecord.md +32 -0
- package/docs/reference/authoring-surface/type-carry.md +19 -0
- package/docs/reference/authoring-surface/type-chainlink.md +3 -3
- package/docs/reference/authoring-surface/type-commandinput.md +4 -3
- package/docs/reference/authoring-surface/type-commandoptions.md +0 -22
- package/docs/reference/authoring-surface/type-commandoutput.md +23 -5
- package/docs/reference/authoring-surface/type-customize.md +20 -3
- package/docs/reference/authoring-surface/type-delivered.md +17 -0
- package/docs/reference/authoring-surface/type-deliveredevent.md +18 -0
- package/docs/reference/authoring-surface/type-dispatchdefinition.md +4 -3
- package/docs/reference/authoring-surface/type-dispatchoutput.md +9 -2
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- package/docs/reference/authoring-surface/type-effectbinding.md +12 -2
- package/docs/reference/authoring-surface/type-filebody.md +4 -8
- package/docs/reference/authoring-surface/type-framepath.md +14 -0
- package/docs/reference/authoring-surface/type-gatebinding.md +15 -0
- package/docs/reference/authoring-surface/type-gatehooks.md +12 -0
- package/docs/reference/authoring-surface/type-gatelook.md +37 -0
- package/docs/reference/authoring-surface/type-gateoutcome.md +32 -0
- package/docs/reference/authoring-surface/type-gatereply.md +19 -0
- package/docs/reference/authoring-surface/type-gateslot.md +21 -0
- package/docs/reference/authoring-surface/type-gateslotinput.md +13 -0
- package/docs/reference/authoring-surface/type-gateslotparts.md +18 -0
- package/docs/reference/authoring-surface/type-gateslots.md +12 -0
- package/docs/reference/authoring-surface/type-gatetypesat.md +13 -0
- package/docs/reference/authoring-surface/type-hostinjectedinput.md +3 -5
- package/docs/reference/authoring-surface/type-httpdefinition.md +6 -3
- package/docs/reference/authoring-surface/type-httpfileinput.md +20 -0
- package/docs/reference/authoring-surface/type-httpoutput.md +8 -2
- package/docs/reference/authoring-surface/type-jr2config.md +37 -2
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- package/docs/reference/authoring-surface/type-lends.md +5 -1
- package/docs/reference/authoring-surface/type-lookoutput.md +14 -0
- package/docs/reference/authoring-surface/type-mcpcalloutput.md +78 -0
- package/docs/reference/authoring-surface/type-mcpnotreturned.md +42 -0
- package/docs/reference/authoring-surface/type-mcpoptions.md +47 -0
- package/docs/reference/authoring-surface/type-mcpoutput.md +50 -0
- package/docs/reference/authoring-surface/type-mcpreadoutput.md +53 -0
- package/docs/reference/authoring-surface/type-mcpremoteserver.md +50 -0
- package/docs/reference/authoring-surface/type-mcpserver.md +41 -0
- package/docs/reference/authoring-surface/type-mcpstdioserver.md +42 -0
- package/docs/reference/authoring-surface/type-namedsecret.md +4 -4
- package/docs/reference/authoring-surface/type-namestart.md +77 -0
- package/docs/reference/authoring-surface/type-noslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-notworktreekeys.md +12 -0
- package/docs/reference/authoring-surface/type-openbodydeclaration.md +1 -1
- package/docs/reference/authoring-surface/type-openbodyoutput.md +1 -1
- package/docs/reference/authoring-surface/type-opencommandinput.md +7 -2
- package/docs/reference/authoring-surface/type-permissivenoslotworkspaceoptions.md +1 -6
- package/docs/reference/authoring-surface/type-permissiveworkspaceoptions.md +5 -10
- package/docs/reference/authoring-surface/type-poolnextinput.md +22 -0
- package/docs/reference/authoring-surface/type-poolspec.md +11 -6
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +33 -8
- package/docs/reference/authoring-surface/type-pushsourcespec.md +38 -0
- package/docs/reference/authoring-surface/type-readsourcespec.md +47 -0
- package/docs/reference/authoring-surface/type-reposlot.md +6 -6
- package/docs/reference/authoring-surface/type-reread.md +18 -0
- package/docs/reference/authoring-surface/type-runsconfig.md +41 -0
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-sender.md +26 -1
- package/docs/reference/authoring-surface/type-sendercallerfield.md +14 -0
- package/docs/reference/authoring-surface/type-size.md +1 -1
- package/docs/reference/authoring-surface/type-skip.md +19 -0
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcehooks.md +23 -0
- package/docs/reference/authoring-surface/type-sourcein.md +25 -0
- package/docs/reference/authoring-surface/type-sourcereply.md +18 -0
- package/docs/reference/authoring-surface/type-sourcespec.md +26 -17
- package/docs/reference/authoring-surface/type-sourceverdict.md +49 -0
- package/docs/reference/authoring-surface/type-standingpoolmachine.md +12 -0
- package/docs/reference/authoring-surface/type-standingpoolspec.md +51 -0
- package/docs/reference/authoring-surface/type-steerargs.md +20 -0
- package/docs/reference/authoring-surface/type-thinkinglevel.md +1 -1
- package/docs/reference/authoring-surface/type-usercontainer.md +1 -1
- package/docs/reference/authoring-surface/type-workspaced.md +30 -9
- package/docs/reference/authoring-surface/type-workspacehandles.md +5 -6
- package/docs/reference/authoring-surface/type-workspaceoptions.md +1 -13
- package/docs/reference/authoring-surface/type-worktreefound.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeinput.md +56 -0
- package/docs/reference/authoring-surface/type-worktreekey.md +14 -0
- package/docs/reference/authoring-surface/type-worktreeoptions.md +29 -0
- package/docs/reference/authoring-surface/type-worktreeoutput.md +45 -0
- package/docs/reference/authoring-surface/type-worktreepath.md +16 -0
- package/docs/reference/authoring-surface/type-worktreeready.md +37 -0
- package/docs/reference/authoring-surface/type-worktreerefused.md +21 -0
- package/docs/reference/authoring-surface/workspace.md +66 -38
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +88 -147
- package/docs/reference/cli.md +413 -74
- package/docs/reference/config.md +248 -6
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +81 -14
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +39 -25
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0007-durable-machine-state.md +1 -1
- package/docs/reference/decisions/0009-cli-and-instance-interface.md +150 -49
- package/docs/reference/decisions/0010-bdd-acceptance-tests.md +5 -3
- package/docs/reference/decisions/0011-workflow-defined-events.md +256 -12
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +39 -32
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +54 -30
- package/docs/reference/decisions/0014-observation-is-open-run-state-is-not.md +53 -27
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +26 -16
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +226 -10
- package/docs/reference/decisions/0018-instance-agents-are-definitions-jr2-assembles-the-harness.md +15 -7
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +53 -27
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +16 -2
- package/docs/reference/decisions/0022-observation-is-a-level-triggered-feed.md +4 -4
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +124 -43
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +36 -38
- package/docs/reference/decisions/0025-cancel-ends-a-run-stop-parks-it.md +10 -5
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +16 -7
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +36 -19
- package/docs/reference/decisions/0029-a-menu-is-fixed-for-a-conversation-and-a-turn-is-told-its-allowed-picks.md +18 -10
- package/docs/reference/decisions/0030-a-snapshot-names-the-machine-it-was-written-under.md +3 -2
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +23 -15
- package/docs/reference/decisions/0032-the-console-unlocks-with-a-callers-credential.md +101 -0
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +22 -22
- package/docs/reference/decisions/0034-the-console-renders-with-preact-and-the-server-erases-the-types.md +6 -6
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +13 -12
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +23 -23
- package/docs/reference/decisions/0050-what-code-names-config-declares-what-the-cli-names-discovery-finds.md +6 -1
- package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md +57 -46
- package/docs/reference/decisions/0052-a-sandbox-lands-where-an-ordinary-pod-lands-and-the-cache-follows-it.md +5 -0
- package/docs/reference/decisions/0053-a-fetch-inside-the-pod-asks-the-node-cache-and-the-cache-asks-the-remote.md +20 -8
- package/docs/reference/decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md +55 -40
- package/docs/reference/decisions/0055-a-release-is-a-pushed-tag-and-the-publish-guard-is-the-credential.md +14 -13
- package/docs/reference/decisions/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +31 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +18 -10
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +137 -86
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +165 -91
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +7 -7
- package/docs/reference/decisions/0064-a-workspace-waits-for-capacity.md +2 -2
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-kept-for-a-set-time.md +109 -0
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +53 -23
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +221 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +161 -96
- package/docs/reference/decisions/0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md +32 -9
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +48 -33
- package/docs/reference/decisions/0071-a-run-starts-a-run-through-a-dispatch-effect.md +56 -30
- package/docs/reference/decisions/0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md +35 -25
- package/docs/reference/decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md +1 -1
- package/docs/reference/decisions/0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md +296 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +347 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions/0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md +285 -0
- package/docs/reference/decisions/0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md +198 -0
- package/docs/reference/decisions.md +9 -4
- package/docs/reference/glossary.md +138 -45
- package/docs/reference/hooks.md +1077 -0
- package/docs/reference/http-api.md +988 -123
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +7 -5
- package/package.json +10 -3
- package/src/actor.ts +194 -42
- package/src/agent.ts +56 -6
- package/src/ambient.ts +3 -5
- package/src/assembly-checks.ts +530 -4
- package/src/callers.ts +245 -0
- package/src/chart.ts +238 -26
- package/src/charts.ts +9 -2
- package/src/config.ts +566 -7
- package/src/conversations.ts +453 -0
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +257 -77
- package/src/customize.ts +154 -7
- package/src/dispatch-effect.ts +142 -26
- package/src/durability.ts +2 -0
- package/src/duration.ts +20 -0
- package/src/effect-checks.ts +52 -4
- package/src/effect.ts +401 -132
- package/src/effector-wire.ts +278 -30
- package/src/fail.ts +17 -4
- package/src/gate-brand.ts +16 -0
- package/src/gate.ts +634 -48
- package/src/harness-client.ts +84 -35
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +197 -39
- package/src/holds.ts +5 -2
- package/src/hook-kinds/discord.ts +92 -0
- package/src/hook-kinds/door.ts +107 -0
- package/src/hook-kinds/github.ts +89 -0
- package/src/hook-kinds/gitlab.ts +72 -0
- package/src/hook-kinds/hook.ts +7 -0
- package/src/hook-kinds/index.ts +465 -0
- package/src/hook-kinds/slack.ts +151 -0
- package/src/hook-kinds/webhook.ts +113 -0
- package/src/hooks.ts +506 -0
- package/src/http-effect.ts +164 -70
- package/src/http.ts +933 -99
- package/src/index.ts +30 -5
- package/src/instance.ts +196 -26
- package/src/internal.ts +23 -3
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +29 -5
- package/src/mcp-effect.ts +424 -0
- package/src/model-keys.ts +24 -0
- package/src/mount-digest.ts +41 -7
- package/src/names.ts +82 -7
- package/src/notices.ts +576 -59
- package/src/open-body.ts +110 -30
- package/src/open-effect.ts +17 -4
- package/src/open.ts +40 -7
- package/src/parts.ts +125 -35
- package/src/party.ts +35 -1
- package/src/pick.ts +43 -9
- package/src/pool.ts +1172 -172
- package/src/registration.ts +249 -17
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +1614 -190
- package/src/run-list.ts +191 -0
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +116 -21
- package/src/secret-placement.ts +248 -35
- package/src/server.ts +53 -4
- package/src/setup.ts +158 -38
- package/src/snapshot-store.ts +565 -48
- package/src/steer.ts +111 -0
- package/src/stub-harness.ts +6 -2
- package/src/tokens.ts +362 -46
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +27 -10
- package/src/workspace.ts +314 -259
- package/src/worktree.ts +249 -0
- package/docs/reference/authoring-surface/type-noslotspec.md +0 -26
- package/docs/reference/authoring-surface/type-workspacespec.md +0 -37
- package/docs/reference/decisions/0032-the-console-unlocks-with-the-instance-token.md +0 -61
- package/docs/reference/decisions/0065-a-persist-survives-a-crash-and-history-is-a-week.md +0 -65
|
@@ -31,11 +31,11 @@ because they are one problem: **what a Machine depends on that is not inside it.
|
|
|
31
31
|
names of the secrets it names — or the `open` sentinel where a package left the url, the argv or a secret's name for
|
|
32
32
|
its composer ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)). `jr2 up` reads them
|
|
33
33
|
without running anything, and a secret named there is what places it.
|
|
34
|
-
- **The Sandbox Image is a `workspace()` option**, static: `workspace(body, { input, image, repos,
|
|
35
|
-
is ADR-0051's, and follows the same rule). `image` is a string in one of two shapes — a `file:` URL
|
|
34
|
+
- **The Sandbox Image is a `workspace()` option**, static: `workspace(body, { input, image, repos, workGroup })`
|
|
35
|
+
(`repos` is ADR-0051's, and follows the same rule). `image` is a string in one of two shapes — a `file:` URL
|
|
36
36
|
(`import.meta.resolve("./image")`, a docker context the module ships) or a registry ref — or absent, which keeps
|
|
37
|
-
ADR-0037's fallback. It
|
|
38
|
-
|
|
37
|
+
ADR-0037's fallback. It is static because `jr2 up` must find it with no run. It is never persisted: the provisioning
|
|
38
|
+
state re-reads it off the Machine on restore.
|
|
39
39
|
- **The definition rides the Turn.** The admission body carries the slot's definition; the Harness runs what it was
|
|
40
40
|
handed and re-reads it per Submission as before. The Instance roster (`agents/`, then `config.agents`), its ConfigMap,
|
|
41
41
|
and `JR2_AGENTS_JSON` retire — a flat roster cannot hold two `researcher`s, and a Machine edit already needs `jr2 up`
|
|
@@ -47,27 +47,27 @@ because they are one problem: **what a Machine depends on that is not inside it.
|
|
|
47
47
|
ADR-0011 adopted for Vocabulary.
|
|
48
48
|
- **Customization is `customize(machine, parts)`**, a plain function in the family of `workspace()` and `pool()`,
|
|
49
49
|
returning a plain `StateMachine`. `parts` has the declaration's own shape, recursively partial:
|
|
50
|
-
`{ agents?: { <slot>?: Partial<AgentDefinition> }, effects?: { <slot>?: { url } | { argv } | { secret } }, image?, user?, repos?: { <slot>?: Binding }, actors?: { <child>?: parts } }`
|
|
50
|
+
`{ agents?: { <slot>?: Partial<AgentDefinition> }, effects?: { <slot>?: { url } | { argv } | { secret } | { server } | { tool } }, image?, user?, repos?: { <slot>?: Binding }, actors?: { <child>?: parts } }`
|
|
51
51
|
— the two image seats together, since ADR-0005 gives the User Container the same static option, and the Repo Slots
|
|
52
52
|
beside them, since ADR-0051 makes a Repo a `workspace()` slot a composer binds. `effects` binds an Effect's Open url,
|
|
53
|
-
argv or secret name, once — an Effect with an Open secret and an Open url or argv takes one
|
|
54
|
-
`{ url, secret }` or `{ argv, secret }`
|
|
55
|
-
([ADR-0054](0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)): the
|
|
56
|
-
again with the bound part and substituted as an Agent override is. Internally an Agent override
|
|
57
|
-
`machine.provide({ actors: { researcher: agent(merged) } })`, with the override layered over the stock
|
|
58
|
-
child override is the same call one level down, recursing — each level is exactly the one-level reach
|
|
59
|
-
`provide` has, held by the composer who owns the child object, never host-side injection. jr2's
|
|
60
|
-
transparent: `customize(research, { agents })` reaches the body through the `body` slot (and `pool()`'s
|
|
61
|
-
any depth), so a consumer never writes `body`. The images and the Repo bindings are the parts `provide`
|
|
62
|
-
(xstate copies only implementations, and every part is keyed on the config it passes through by
|
|
63
|
-
fields clone the wrapper's config and rebuild it with the same implementations, re-attaching what
|
|
64
|
-
carried. A bound argv's load warnings are such a part too: `customize()` rebuilds the Machine the author
|
|
65
|
-
same way, with the vocabulary, the seats and the door it carried and the warnings the walk gives for the
|
|
66
|
-
so no attachment is keyed on anything but the config. Only DECLARED parts can be retuned, and the compiler
|
|
67
|
-
says so: the slots are read off xstate's own `TActor` parameter, so an Agent, a Repo Slot, or a child the
|
|
68
|
-
not carry is a type error (ADR-0050), and the runtime refuses the same call naming the Machine's own
|
|
69
|
-
a `.with()` method on the machine — it needs a jr2-owned machine type over xstate's, which ADR-0015
|
|
70
|
-
vanishes after any `.provide()`; a callable-machine hybrid — verified to work, reads as a trick.
|
|
53
|
+
argv, MCP server and tool, or secret name, once — an Effect with an Open secret and an Open url or argv takes one
|
|
54
|
+
object with both keys, `{ url, secret }` or `{ argv, secret }`, and an `mcp` Effect with an Open server and tool takes
|
|
55
|
+
`{ server, tool }` ([ADR-0054](0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)): the
|
|
56
|
+
slot's logic is built again with the bound part and substituted as an Agent override is. Internally an Agent override
|
|
57
|
+
is xstate's `machine.provide({ actors: { researcher: agent(merged) } })`, with the override layered over the stock
|
|
58
|
+
definition; a child override is the same call one level down, recursing — each level is exactly the one-level reach
|
|
59
|
+
ADR-0015 found `provide` has, held by the composer who owns the child object, never host-side injection. jr2's
|
|
60
|
+
wrappers are transparent: `customize(research, { agents })` reaches the body through the `body` slot (and `pool()`'s
|
|
61
|
+
`worker`, at any depth), so a consumer never writes `body`. The images and the Repo bindings are the parts `provide`
|
|
62
|
+
cannot carry (xstate copies only implementations, and every part is keyed on the config it passes through by
|
|
63
|
+
reference), so those fields clone the wrapper's config and rebuild it with the same implementations, re-attaching what
|
|
64
|
+
the original carried. A bound argv's load warnings are such a part too: `customize()` rebuilds the Machine the author
|
|
65
|
+
wrote the same way, with the vocabulary, the seats and the door it carried and the warnings the walk gives for the
|
|
66
|
+
bound argv, so no attachment is keyed on anything but the config. Only DECLARED parts can be retuned, and the compiler
|
|
67
|
+
is what says so: the slots are read off xstate's own `TActor` parameter, so an Agent, a Repo Slot, or a child the
|
|
68
|
+
Machine does not carry is a type error (ADR-0050), and the runtime refuses the same call naming the Machine's own
|
|
69
|
+
slots. Rejected: a `.with()` method on the machine — it needs a jr2-owned machine type over xstate's, which ADR-0015
|
|
70
|
+
avoided, and it vanishes after any `.provide()`; a callable-machine hybrid — verified to work, reads as a trick.
|
|
71
71
|
- **A package exports a Machine, nothing beside it.** The door (ADR-0033), the vocabulary, the Agents, and the image all
|
|
72
72
|
ride the exported object. The three uses:
|
|
73
73
|
|
|
@@ -17,7 +17,12 @@ a definition lives, and the gate that enforces it.
|
|
|
17
17
|
an Effect names it with `secret("name")`, and the Instance declares it in the top-level `secrets` list
|
|
18
18
|
([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)). The name is a string, not a type: the
|
|
19
19
|
walk after the typecheck refuses an Effect that names a secret the list does not hold, before anything is built.
|
|
20
|
-
`jr2.config.ts` holds reach and credentials (`harness`, `secrets`, `git.credentials`, `registry
|
|
20
|
+
`jr2.config.ts` holds reach and credentials (`harness`, `secrets`, `git.credentials`, `registry`, and the Callers'
|
|
21
|
+
`callers`, `hooks` and `cors`,
|
|
22
|
+
[ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). A Machine names no Hook: a
|
|
23
|
+
Gate's or a Source's handlers are keyed by the Hook's kind ([ADR-0011](0011-workflow-defined-events.md)), and only a
|
|
24
|
+
Hook's address names it. A Hook kind is code that the config and the Workflows import by name, so it is neither
|
|
25
|
+
declared nor discovered: it comes in by `import`, as a nested Machine does, and `jr2 up` bundles it into the Assembly.
|
|
21
26
|
- **The slot key types a Repo.** A `workspace()` declares its Repos as slots —
|
|
22
27
|
`repos: { target: open, docs: { url, ref } }` — and the keys are a phantom on the wrapper type, read through
|
|
23
28
|
`pool()`'s `worker` and `workspace()`'s `body` as the image is (ADR-0049). A
|
package/docs/reference/decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# A Repo is a slot on the `workspace()` and a cache on the node
|
|
2
2
|
|
|
3
3
|
ADR-0049 moved Agents and the Sandbox Image into the Machine and left one dependency outside it: the Repo, catalogued in
|
|
4
|
-
`jr2.config.ts` and named by string from a `workspace()
|
|
4
|
+
`jr2.config.ts` and named by string from a `workspace()`, with the name typed back through a `Register` interface
|
|
5
5
|
(ADR-0050). The grill of 2026-09-13 found two faults. A packaged Machine still could not say which repository it works
|
|
6
6
|
on, nor could a consumer bind one, except by the consumer's config holding the right NAME and the door threading it —
|
|
7
7
|
the same encapsulation gap ADR-0049 closed for Agents. And the catalog's shape was dictated by storage: one read-only
|
|
@@ -20,28 +20,32 @@ decision: **what a Machine says about a repository, and what the cluster does wi
|
|
|
20
20
|
target: open, // the consumer's — must be bound
|
|
21
21
|
docs: { url: "https://github.com/acme/handbook.git", ref: "v3" }, // the package's own
|
|
22
22
|
},
|
|
23
|
-
spec: ({ input }) => ({ branch: input.branch }),
|
|
24
23
|
});
|
|
25
24
|
```
|
|
26
25
|
|
|
27
26
|
A slot is in one of three states, and the walk tells them apart without evaluating anything: **bound** (a url, or
|
|
28
27
|
`{ url, ref? }`), **open** (the `open` sentinel — someone downstream binds it), or **per-run** (a mapper over the
|
|
29
28
|
wrapper's door: `feature: ({ input }) => ({ url: input.feature.repo, ref: input.feature.baseRef })`). `ref` is the
|
|
30
|
-
base
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
Binding's base: `default/` stays on it, and a new branch starts from it. A branch is attached, and a tag or a commit
|
|
30
|
+
is detached. Absent, the base is the repository's own default branch. The body's handles are keyed by slot
|
|
31
|
+
(`workspace.repos.target` is `/work/target`), and so are the in-pod paths (`/work/<slot>/<name>`): a slot key is
|
|
32
|
+
collision-free by construction and the same in every Instance that consumes the package, so a prompt can name a path
|
|
33
|
+
and be right everywhere.
|
|
35
34
|
|
|
36
|
-
- **The handles are the slot map
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
- **The handles are the slot map — no slot is the `workdir`.** `workspace.repos` keeps the slots in declaration order,
|
|
36
|
+
and jr2 reads nothing into that order. A handle is the slot's folder and not a Worktree: the Workspace only clones,
|
|
37
|
+
and a Machine cuts each Worktree at `/work/<slot>/<name>`
|
|
38
|
+
([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)). Which checkout an Agent works in is a
|
|
39
|
+
fact about that Agent's Turn, not about the Workspace: a body with a `backend` and a `frontend` slot frames one Agent
|
|
40
|
+
with a Worktree of the first and another with a Worktree of the second, and either may read the other's tree. An
|
|
41
|
+
Effect names its Worktree as a Turn does, in its input: a `command` and an `http` take no `slot`, and a `push` keeps
|
|
42
|
+
its static `slot`, which says the Repo Slot whose credential it spends. A slot that is only read needs no Worktree: an
|
|
43
|
+
Agent reads `/work/<slot>/default`. A body that names no slot may give the order a meaning of its own and document it
|
|
44
|
+
(`task`: the first slot is the one the coder edits). The order is the kit's promise — a slot key starts with a letter,
|
|
45
|
+
because JS reads an integer-like key first wherever it was written — and the meaning is the Machine's. Rejected: a
|
|
46
|
+
`workdir` handle, the first declared slot's worktree — it made a singular "primary repo" a property of every
|
|
47
|
+
Workspace, which is wrong for a body whose Agents work in different slots or in more than one, and it hid a rule in
|
|
48
|
+
key order that the body never chose.
|
|
45
49
|
|
|
46
50
|
- **A Machine whose body names no slot declares the whole map Open: `repos: open`.** A body that enumerates whatever is
|
|
47
51
|
attached — `@jr2/machines`'s `task` — knows nothing a slot name could say, and a `target: open` on it would claim "one
|
|
@@ -61,21 +65,18 @@ decision: **what a Machine says about a repository, and what the cluster does wi
|
|
|
61
65
|
have a second Agent edit it, upload it. Its steps share `/work`, as the steps of one GitHub Actions job share that
|
|
62
66
|
job's folder; two Workspaces are two jobs and share nothing. So `workspace(body, { input })` with no `repos` is a
|
|
63
67
|
Workspace with no Repo Slot: the attach is skipped (there is nothing to clone, and the pod's `fsGroup` already makes
|
|
64
|
-
`/work` the work group's), the body's handles are `{ repos: {} }
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Lost Sandbox takes everything with it — with a repository, pushed commits outlive the pod; here only what an Effect
|
|
74
|
-
sent out does, and the body hears `workspace.lost` as any body does. Storage that outlives the pod, the job's
|
|
68
|
+
`/work` the work group's), the body's handles are `{ repos: {} }`, typed `Workspaced<…, never>`, and a Turn that
|
|
69
|
+
frames no `cwd` and a `command` both work in `/work`. `repos: {}` is refused with the pointer to leaving it out, so
|
|
70
|
+
"none" has one spelling and an empty map never reads as one somebody forgot to fill. An Open map still asks
|
|
71
|
+
`customize` for at least one slot: Open says the composer names the slots, and a body that enumerates its checkouts
|
|
72
|
+
(`task`) has nothing to do with none. Three Effects need a slot and are refused there, by `jr2 up` and at invoke, for
|
|
73
|
+
one reason: a `worktree()` cuts in a slot's clone, a `push` publishes a slot's branch, and a `command` with an Effect
|
|
74
|
+
secret runs against one commit of it (ADR-0067). A file-body `http` reads `/work` and stays. `/work` is an `emptyDir`,
|
|
75
|
+
so a Lost Sandbox takes everything with it — with a repository, pushed commits outlive the pod; here only what an
|
|
76
|
+
Effect sent out does, and the body hears `workspace.lost` as any body does. Storage that outlives the pod, the job's
|
|
75
77
|
"artifacts", is a decision of its own. Rejected: a placeholder repository bound to satisfy the rule — a clone nobody
|
|
76
|
-
uses, a
|
|
77
|
-
|
|
78
|
-
needed.
|
|
78
|
+
uses, a Worktree that means nothing, a `cwd` that each Turn must then state, and a cache entry to warm. Rejected: a
|
|
79
|
+
new term for the repository-less Sandbox — the Glossary's Workspace never named a repository, so none was needed.
|
|
79
80
|
|
|
80
81
|
- **The consumer binds with `customize`**, in the same call as Agents and the image, nested through `actors` like every
|
|
81
82
|
other part: `customize(codeReview, { repos: { target: "git@github.com:ourorg/app.git" } })`. Any of the three forms is
|
|
@@ -89,6 +90,14 @@ decision: **what a Machine says about a repository, and what the cluster does wi
|
|
|
89
90
|
is the thing, not a reference into someone else's file. Two spellings of one repository (`https://`, `git@…:`,
|
|
90
91
|
`ssh://`, with or without `.git`) resolve to one identity — host plus path, scheme and user dropped — and therefore
|
|
91
92
|
one cache. A door that offers a menu of repositories is the Instance's own `z.enum` of urls.
|
|
93
|
+
- **The cache key names one identity.** The key is the Repo CR's name, the hostPath folder, the volume name `repo-<key>`
|
|
94
|
+
and the mount `/repos/<key>`: a slug of the identity's last segment, at most 16 characters, a dash, and the first 32
|
|
95
|
+
hex digits (128 bits) of the identity's sha256, at most 49 characters in all. No one can find two identities with one
|
|
96
|
+
key, and a per-run url is the Agent's text, so a shorter hash is a way into another Repo's cache: with 32 bits, a
|
|
97
|
+
search found a url of an attacker's repository with the key of a private one in seconds. Each Repo CR states its
|
|
98
|
+
identity (`jr2.dev/identity`). The Orchestrator refuses to write a CR whose stated identity is not the one that it
|
|
99
|
+
attaches or binds, and names both urls: no url, no `secretRef` and no clock of one repository is written on a CR of
|
|
100
|
+
another.
|
|
92
101
|
- **The cluster keeps a bare cache per node, not a volume.** Each repository is a `Repo` custom resource the operator
|
|
93
102
|
reconciles (spec: url, refresh interval; status: per node — synced, generation, last error). A cache agent runs as a
|
|
94
103
|
DaemonSet per Instance over its Sandbox nodes (ADR-0052) with a hostPath directory: it clones a repository onto its
|
|
@@ -100,9 +109,8 @@ decision: **what a Machine says about a repository, and what the cluster does wi
|
|
|
100
109
|
on-demand fetch; every cache speaks git protocol v2, so a fetch advertises only the refs it asks for; and the refresh
|
|
101
110
|
interval carries ±20% jitter, like the Lease, so R Repos on N nodes do not hit a remote on one beat (scaling review
|
|
102
111
|
R13, 2026-09-30). The agent also consolidates a cache's packs without deleting an object (ADR-0004). The
|
|
103
|
-
Orchestrator's attach then runs unchanged: `git clone --shared` off the mount
|
|
104
|
-
|
|
105
|
-
run on any node.
|
|
112
|
+
Orchestrator's attach then runs unchanged: `git clone --shared` off the mount. A Sandbox that lands on a cold node
|
|
113
|
+
pays one clone there, once — the same economics as an image pull, and the reason a Sandbox can run on any node.
|
|
106
114
|
- **The Orchestrator creates `Repo` CRs; it does not sync them.** At boot it walks its registered Machines and creates
|
|
107
115
|
one CR per bound identity, so a statically known repository is KNOWN before a run can ask: the cache agent on every
|
|
108
116
|
node probes it (`git ls-remote`) as soon as the CR exists, so a wrong url or a credential that does not reach shows in
|
|
@@ -123,10 +131,13 @@ decision: **what a Machine says about a repository, and what the cluster does wi
|
|
|
123
131
|
whose clone Secret `git-clone.json` does not list gets none, and the clone is anonymous; the cache agent reads only
|
|
124
132
|
that Secret, and the operator matches nothing. The Orchestrator writes and reads no git credential (ADR-0068). The
|
|
125
133
|
Secret uses Flux's key names (`username`/`password`; `identity`, `identity.pub`, `known_hosts`), so a Flux or Argo
|
|
126
|
-
user reuses the Secret they have.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
user reuses the Secret they have. With a token, the cache agent's helper answers for the Repo's own origin alone
|
|
135
|
+
(scheme, host and port), and its clone, probe and fetch follow no redirect, so the token never reaches an origin
|
|
136
|
+
outside the entry. A repository that moved, and that its old url redirects to, is cloned by its new url. **The list is
|
|
137
|
+
also the fence.** A per-run url — a run input, a ticket field — is otherwise a way to spend the cluster's credential
|
|
138
|
+
against any host: one that matches no entry is refused at attach, naming the list. A static binding is code the
|
|
139
|
+
Instance typechecked and deployed, so it is admitted without a match and cloned anonymously; a private one then fails
|
|
140
|
+
at the cache with git's error. `jr2 init` scaffolds one wildcard entry —
|
|
130
141
|
`{ match: "*", token: "JR2_GIT_TOKEN", sshKey: "jr2-git-ssh" }`, today's two implicit defaults made visible, with a
|
|
131
142
|
comment saying to narrow it before anything untrusted can start a run, and that it also pushes: a `push()` whose Repo
|
|
132
143
|
Slot url it matches, a per-run url included, spends the same token or key — so the open fence is in the user's file
|
|
@@ -198,15 +209,15 @@ decision: **what a Machine says about a repository, and what the cluster does wi
|
|
|
198
209
|
the kit's first cluster requirement a storage class most small clusters lack.
|
|
199
210
|
- **A Flux-style `GitRepository` serving an artifact over HTTP.** Rejected: an artifact download is a clone per Sandbox
|
|
200
211
|
— the cost the cache exists to avoid.
|
|
201
|
-
- **Urls
|
|
202
|
-
walk cannot see inside
|
|
203
|
-
would have nothing to bind.
|
|
212
|
+
- **Urls from a per-run function of the `workspace()`, not the slot** (the shape before this decision, with a url in
|
|
213
|
+
place of a name). Rejected: the walk cannot see inside a function, so a bound repository would be invisible to
|
|
214
|
+
`jr2 up` and to prewarm, and `customize` would have nothing to bind.
|
|
204
215
|
|
|
205
216
|
## Consequences
|
|
206
217
|
|
|
207
218
|
- The operator becomes git-aware, which ADR-0001 kept it from being: the `Repo` CRD and the cache agent are its. The
|
|
208
|
-
Sandbox CRD stays free of git semantics beyond a list of identities; clone
|
|
209
|
-
|
|
219
|
+
Sandbox CRD stays free of git semantics beyond a list of identities; the clone remains the Orchestrator's post-`Ready`
|
|
220
|
+
step, and a Worktree is a Machine's cut (ADR-0004).
|
|
210
221
|
- `workspace()`'s `repos` names at least one slot, or is `open` whole — in which case the `customize` that names the map
|
|
211
222
|
is held to the same rule — or is left out, for a Workspace with no Repo Slot; a Machine that composes no Sandbox has
|
|
212
223
|
no repos to customize, and the data-plane switch ("does this Instance need Sandboxes") is "a registered Machine
|
|
@@ -214,12 +225,12 @@ decision: **what a Machine says about a repository, and what the cluster does wi
|
|
|
214
225
|
Sandboxes, the cache agent and the Sandbox Images; a deployed Orchestrator wires its kube client whatever its current
|
|
215
226
|
Assembly composes, because a publish can bring the first `workspace()` without a roll
|
|
216
227
|
([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)).
|
|
217
|
-
-
|
|
218
|
-
|
|
228
|
+
- A slot has no read-only mode, and needs none: a slot in which a Machine cuts nothing holds only `default/`, on the
|
|
229
|
+
Binding's base.
|
|
219
230
|
- The Console's start form loses the catalog dropdown: a per-run repository is a url field unless the Machine's door
|
|
220
231
|
enumerates its own.
|
|
221
|
-
- The volume paths change: the in-pod source mount is `/repos/<key>` where `<key>` is
|
|
222
|
-
pod-local layout is `/work/<slot>/{default,<
|
|
232
|
+
- The volume paths change: the in-pod source mount is `/repos/<key>` where `<key>` is the identity's cache key, and the
|
|
233
|
+
pod-local layout is `/work/<slot>/{default,<name>}`. Existing checkouts on a pre-0051 PVC are not migrated.
|
|
223
234
|
- ADR-0047's deploy-key prompt reads ssh urls off the walk. A per-run ssh url first seen at attach was never prompted
|
|
224
235
|
for; its first attach fails with the key hint, as any unregistered key does.
|
|
225
236
|
- A `customize` of a per-run slot with a static url is legal and makes it bound; a Machine that wants a slot to stay the
|
|
@@ -27,6 +27,11 @@ use. **Which nodes are an Instance's Sandbox nodes, and who follows that set** i
|
|
|
27
27
|
Sandbox node, so a cache is only ever present where a Sandbox can land, and the affinity it feeds only ever names a
|
|
28
28
|
usable node. The build set (ADR-0045) is the union of the nodes an ordinary pod lands on — the Orchestrator
|
|
29
29
|
Deployment's own placement — and the Sandbox nodes, intersected with the supported set as before.
|
|
30
|
+
- **The cache agent also reports Landlock for its node.** It probes Landlock when it starts, and on a node without it
|
|
31
|
+
the agent is NotReady, with the reason. `jr2 up` reads the DaemonSet's pods and refuses to converge while a Sandbox
|
|
32
|
+
node lacks Landlock, naming each one ([ADR-0077](0077-jr2-requires-landlock-on-every-sandbox-node.md)). This is the
|
|
33
|
+
one refusal on the node set: Landlock is a fact that each Sandbox node must have, not a set that can be empty. The
|
|
34
|
+
admin keeps a bad node out with `sandbox.nodeSelector` or a taint.
|
|
30
35
|
- **The Sandbox CR carries the placement.** `Sandbox.spec` gains `nodeSelector` and `tolerations` beside `resources`,
|
|
31
36
|
`initContainers`, and `volumes`; the Orchestrator writes them from its baked config at provision and the operator
|
|
32
37
|
copies them onto the pod, merging nothing. The operator stays config-blind (ADR-0001), and every Instance's placement
|
|
@@ -29,8 +29,8 @@ a fetch inside a pod reaches the remote, and who may ask** is one decision.
|
|
|
29
29
|
Binding's own spelling, the caller's own credential, never the Agent's (ADR-0005).
|
|
30
30
|
|
|
31
31
|
- **The ask rides the Sandbox CR and reaches the agent through the pod.** The program asks the Custodian on `localhost`
|
|
32
|
-
(`POST /fetch`, with the
|
|
33
|
-
puts the token
|
|
32
|
+
(`POST /fetch`, with the fetch token's Stand-in — a constant, because the User Container gets no env); the Custodian
|
|
33
|
+
puts the fetch token in place of the Stand-in, on this route alone and asks the Orchestrator for THIS pod's Sandbox
|
|
34
34
|
(`POST /sandboxes/<name>/fetch` — the program cannot name another,
|
|
35
35
|
[ADR-0059](0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md)); the Orchestrator patches one
|
|
36
36
|
annotation on the Sandbox CR, per Repo key, timestamp value — the Lease's shape, an annotation the Orchestrator
|
|
@@ -45,16 +45,28 @@ a fetch inside a pod reaches the remote, and who may ask** is one decision.
|
|
|
45
45
|
sticky, once per pod life. The Orchestrator's route waits on that entry — a Sandbox watch event, never a poll
|
|
46
46
|
([ADR-0063](0063-the-orchestrator-watches-the-cluster.md)) — then answers the Custodian, which answers the program.
|
|
47
47
|
One wait, and it is the wait an attach already makes.
|
|
48
|
-
- **The scope is the
|
|
49
|
-
and
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
the
|
|
48
|
+
- **The scope is the fetch token's scope, and no seat gains a credential.** The fetch token is valid on the fetch route
|
|
49
|
+
alone, and the control token is not valid there
|
|
50
|
+
([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)). A Sandbox may ask for the
|
|
51
|
+
caches it mounts and nothing else; the remote is reached by the cache agent with the Repo CR's `secretRef`, the
|
|
52
|
+
credential that cloned the cache. The pod holds none before and none after. The Menu is untouched — a fetch is not
|
|
53
|
+
something the Agent _says_, it is something it _does_ — and no Machine declares anything: the ability is ambient in
|
|
54
|
+
every Workspace pod, the way Working tools are, and absent on the Instance Harness, which mounts no Repo.
|
|
53
55
|
- **Freshness degrades, absence does not, and the caller is told.** A remote fetch that fails or outruns the on-demand
|
|
54
56
|
budget lets the program fall through to the objects the cache holds — ADR-0051's stance for attach — and writes one
|
|
55
57
|
line to stderr, which git passes through verbatim:
|
|
56
58
|
`warning: jr2: remote fetch failed (<git's error>); serving the cache as of <lastFetched>`. The budget is the cache
|
|
57
|
-
agent's one on-demand number; the program's wait is that plus watch slack, and nothing else owns a timeout.
|
|
59
|
+
agent's one on-demand number; the program's wait is that plus watch slack, and nothing else owns a timeout. A cut
|
|
60
|
+
reads that line too: its output carries the line in `stale`, and the cut uses what the clone holds. A branch name that
|
|
61
|
+
the clone does not hold is then `refused`, because the remote cannot confirm that it is new
|
|
62
|
+
([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)).
|
|
63
|
+
- **One fetch at a time in a clone.** All Worktrees of a clone share `refs/remotes/origin/*`, and git updates a ref only
|
|
64
|
+
if it still holds the value that the fetch read. So two fetches that overlap on a moved remote can fail with
|
|
65
|
+
`cannot lock ref`. The failure does no harm, but it costs a Turn. So the program takes a per-clone lock after the ask,
|
|
66
|
+
so a dead remote does not make coders wait in line. `git upload-pack` can end before the fetch writes its refs: that
|
|
67
|
+
happens when a pack moves. In a shared clone, git often keeps the connection open to the end. The lock must last to
|
|
68
|
+
the end of the fetch in both cases, so a small child process that the program leaves watches the fetch and holds the
|
|
69
|
+
lock. ADR-0075 holds the measurements.
|
|
58
70
|
- **Every seat that holds the checkouts holds the program.** The fetch url lives in the shared `default/.git/config`, so
|
|
59
71
|
a seat without the program has checkouts whose `git fetch` dies. That is ADR-0005's own argument for `/repos` — half
|
|
60
72
|
of one exception, not a second — applied once more: the pod's `/opt/jr2` volume is mounted read-only into the User
|
|
@@ -31,48 +31,63 @@ the one part ADR-0049/0051 left a package unable to leave unsaid: **the model.**
|
|
|
31
31
|
who never reads the file spends money on a model they did not choose, silently, which is the failure class `jr2 up`
|
|
32
32
|
exists to make loud. Rejected: a Machine factory `task({ model })` — ADR-0049 settled that a package exports a Machine
|
|
33
33
|
and retunes with `customize`, and a factory reopens that.
|
|
34
|
-
- **Open widens to an Effect's url, argv and secret, bound once.** A package cannot know the host
|
|
35
|
-
calls, the program its `command` runs
|
|
34
|
+
- **Open widens to an Effect's url, argv, MCP server and tool, and secret, bound once.** A package cannot know the host
|
|
35
|
+
its `http` Effect calls, the program its `command` runs, the MCP server and tool its `mcp` Effect calls
|
|
36
36
|
([ADR-0067](0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)), or the secret its
|
|
37
37
|
composer spends, so it may leave each Open: `http({ method, url: open, auth, input, request })`,
|
|
38
|
-
`http({ method: "PUT", url: open, body: file(path) })`, `command(open, { env, pin })`,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
`
|
|
43
|
-
|
|
44
|
-
(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
`
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
and
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
38
|
+
`http({ method: "PUT", url: open, body: file(path) })`, `command(open, { env, pin })`,
|
|
39
|
+
`mcp.call(open, open, { input })`, and `secret(open)` in place of a secret's name. The url, the argv, an `mcp`
|
|
40
|
+
Effect's server and tool, and the secret's name are the Open parts of an Effect that exist; everything else the
|
|
41
|
+
definition says is the package's as written — the method, the headers, the auth and the secrets they name, the input,
|
|
42
|
+
`request`, the env, `pin` — and is checked when the Machine is built. `secret(open)` is taken by `bearer`, `basic`
|
|
43
|
+
(the user or the password), a header's value, and a command's env, as its value or its `.path`; in a url it is refused
|
|
44
|
+
when the Machine is built ("leave the whole url Open"), since a url that carries a secret is left Open whole (below).
|
|
45
|
+
An Effect takes one Open secret: a second `secret(open)` in one definition is refused when the Machine is built ("an
|
|
46
|
+
Effect takes one Open secret"); the one `secret(open)` may stand in several places, a value and its `.path`. An Effect
|
|
47
|
+
that names an Open secret is placed as one that names a secret is — a credentialed command in the Sandbox Effector, an
|
|
48
|
+
`http` where its body places it — before the name is known. The composer binds the parts with
|
|
49
|
+
`customize(machine, { effects: { <slot>: { url } } })`, `{ argv }`, `{ secret }`, `{ server }` or `{ tool }`, and an
|
|
50
|
+
Effect with two Open parts takes one object with both keys (`{ url, secret }`, `{ argv, secret }`,
|
|
51
|
+
`{ server, tool }`): a url is a string or a `text` with a secret in its path, an argv is the usual list, and a secret
|
|
52
|
+
is a name that obeys `secret()`'s own rule — it is the secret's file in the Effector's mount (ADR-0068) — and a server
|
|
53
|
+
is a name of the `mcp` block of `jr2.config.ts`, whose secret is the server's own, so an `mcp` Effect takes no
|
|
54
|
+
`secret(open)`
|
|
55
|
+
([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)). The
|
|
56
|
+
type of `effects` is the intersection of the Open parts' bindings, so a binding that leaves one of them unbound is a
|
|
57
|
+
compile error, as an unbound url is; at run time, a binding that leaves one unbound gives an Effect still Open in it,
|
|
58
|
+
which a later `customize()` may bind. Each is bound ONCE, to a value: never a mapper over the door, so a host is fixed
|
|
59
|
+
in the definition or bound before any run, and no run chooses one, nor a secret. Binding builds the Effect again with
|
|
60
|
+
its constructor, with the bound name in every place `secret(open)` stood, so a bound part meets every rule the
|
|
61
|
+
constructor checks — a literal host, no secret in a query, a non-empty argv, a secret's name — and the bound Effect is
|
|
62
|
+
the one the same definition written with that name gives; `jr2 up` reads it as written: the placement, the secrets it
|
|
63
|
+
names, and the Effector's fence ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)) are the
|
|
64
|
+
bound ones, and the bound secret is placed only in that Effect's Effector, as a named secret is. Only an Open part is
|
|
65
|
+
offered: a part the author fixed is no key of `effects` and is refused at the call, and a part bound once is fixed
|
|
66
|
+
from then on, so a second composer cannot move it. `effects` reaches through `workspace()`'s body, `pool()`'s worker
|
|
67
|
+
and a composed Machine's `actors`, as `agents` does, typed off the slot's logic. An Open argv's slots are the
|
|
68
|
+
composer's, so the load walk counts every pick param as read by it when the Machine is built, and `customize()` runs
|
|
69
|
+
the walk's pick-param check again once the argv is bound — a param the bound argv does not read is refused there, and
|
|
70
|
+
a warning it gives for the bound argv is the bound Machine's, as it is the same Machine's written with that argv: the
|
|
71
|
+
bound Machine gets a config of its own, which carries those warnings
|
|
72
|
+
([ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md)), so a copy `provide()` makes of it — a run's
|
|
73
|
+
— keeps them, and the Open Machine keeps its own. The compiler's check does not reach a bound argv: an Open argv's
|
|
74
|
+
input is one string per key, so nothing at build or at `customize()` checks that the invoke supplies each slot the
|
|
75
|
+
bound argv names, and a slot it does not supply faults when the Effect starts — its state's `onError`, with nothing
|
|
76
|
+
run. The `jr2 up` walk reports an Open Effect beside Open Repo Slots and Agents and refuses the converge naming the
|
|
77
|
+
`effects` line — `{ effects: { <slot>: { secret: "<name>" } } }` for an Open secret, one object with both keys for an
|
|
78
|
+
Effect with two Open parts, `{ effects: { <slot>: { server: "<name>" } } }`, `{ tool: "<tool>" }` or both for an `mcp`
|
|
79
|
+
Effect, with only its Open keys; the Effect refuses to run while Open, as the second fence — its state's `onError`,
|
|
80
|
+
with nothing sent and no ledger row. The Open part is the whole url, the whole argv, an MCP server's name or a tool's
|
|
81
|
+
name, or the secret's name.
|
|
82
|
+
- **Open widens to a hooked Gate's look, which only warns.** A package writes the Hook handlers of its Gate slots for
|
|
83
|
+
each forge that it supports, because a forge's payload is public, so a handler is never Open. It imports the Hook kind
|
|
84
|
+
of each forge that it hears, for the payload types, and it has `@jr2/orchestrator`, which holds the kinds, as a peer
|
|
85
|
+
dependency ([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). Which Hooks of
|
|
86
|
+
those kinds exist is the Instance's config. A look needs a url and a token, so a package leaves `look: open`, and the
|
|
87
|
+
composer binds it once, to a look or `"none"`, with `customize(machine, { gates: { <slot>: { look } } })`, which can
|
|
88
|
+
also add, replace or remove a handler by kind and event key ([ADR-0011](0011-workflow-defined-events.md)). An Open
|
|
89
|
+
look that nobody binds is the one Open part that `jr2 up` does not refuse: it warns, as it warns about a hooked Gate
|
|
90
|
+
with no look, because the Gate still works when a person answers it with `jr2 send`.
|
|
76
91
|
- **The per-run Dial is the escape hatch, not the default.** A shipped Machine's door may carry `model` and
|
|
77
92
|
`thinkingLevel` (ADR-0018's two Dials) and fold them into each invoke's input; the bound definition is what `jr2 up`
|
|
78
93
|
preflights, the door value overrides it for one run's Turns. Nothing in jr2 changes for this — it is the Machine's own
|
|
@@ -16,10 +16,10 @@ version, images, gate, trigger.
|
|
|
16
16
|
an npmjs credential is the release job, and it holds no token: it publishes with npm trusted publishing (OIDC from
|
|
17
17
|
GitHub Actions), so nothing long-lived exists to leak. A `pnpm -r publish` on a dev box reaches npmjs and fails
|
|
18
18
|
`ENEEDAUTH`, which is the guard — an absence that cannot be edited past, where the manifest line was a presence that
|
|
19
|
-
had to be. `publishConfig.access: public` stays on
|
|
20
|
-
it. `scripts/dist-publish.sh` passes `--registry` again, and `publish.test.ts` inverts: it asserts that
|
|
21
|
-
manifest names a registry, so the old guard cannot be smuggled back in one package and silently pin a
|
|
22
|
-
localhost.
|
|
19
|
+
had to be. `publishConfig.access: public` stays on every public package, because a scoped package's first publish is
|
|
20
|
+
402 without it. `scripts/dist-publish.sh` passes `--registry` again, and `publish.test.ts` inverts: it asserts that
|
|
21
|
+
**no** public manifest names a registry, so the old guard cannot be smuggled back in one package and silently pin a
|
|
22
|
+
release to localhost.
|
|
23
23
|
- **Lockstep, and `main` carries the next version.** One number across `packages/*` and `templates/default`, because
|
|
24
24
|
[ADR-0019](0019-one-converging-command-against-the-current-context.md)'s one release train already reads a single
|
|
25
25
|
`KIT_VERSION`, ADR-0043's scaffold pins two packages at one exact number, and `workspace:*` packs to an exact
|
|
@@ -50,11 +50,11 @@ version, images, gate, trigger.
|
|
|
50
50
|
([ADR-0072](0072-the-docs-ship-inside-the-kit-and-an-instances-agents-md-indexes-them.md)). It does not wait for the
|
|
51
51
|
npm approval, so the site can be ahead of the live packages until the maintainer approves them.
|
|
52
52
|
- **The job stages; the maintainer approves.** Each trusted publisher is configured for `npm stage publish` only, so the
|
|
53
|
-
tag produces
|
|
54
|
-
them live. This is the one human act a release keeps, and it is kept on purpose: the job's identity is
|
|
55
|
-
repository and the workflow file, so a stolen GitHub session or a bad edit to that file could otherwise
|
|
56
|
-
the project's name; staged, it can stage and nothing more. It is not a runbook step — nothing has to be
|
|
57
|
-
order, the staged tarballs wait.
|
|
53
|
+
tag produces one staged tarball per public package and one 2FA approval per package (`npm stage approve`, dependencies
|
|
54
|
+
first) makes them live. This is the one human act a release keeps, and it is kept on purpose: the job's identity is
|
|
55
|
+
bound to the repository and the workflow file, so a stolen GitHub session or a bad edit to that file could otherwise
|
|
56
|
+
publish under the project's name; staged, it can stage and nothing more. It is not a runbook step — nothing has to be
|
|
57
|
+
remembered in order, the staged tarballs wait.
|
|
58
58
|
- **Two executors of one train.** `scripts/publish.sh` is the irreversible half — images, then packages, dependencies
|
|
59
59
|
first, live versions skipped — and the job runs it with `--stage`. A maintainer runs it as `just publish` from the
|
|
60
60
|
pushed tag when a 0.x release cannot wait the job's hour: `npm publish` with a 2FA prompt per package in place of an
|
|
@@ -94,14 +94,15 @@ version, images, gate, trigger.
|
|
|
94
94
|
configuration" no longer holds; the rest of that list does.
|
|
95
95
|
- **Bootstrap, once:** create the `jr2` org on npmjs (a scope is a user or an org), publish the first version by hand
|
|
96
96
|
with a token — npm attaches a trusted publisher only to a package that already exists — then configure the trusted
|
|
97
|
-
publisher on
|
|
98
|
-
the
|
|
99
|
-
|
|
97
|
+
publisher on each public package and revoke the token. Each new public package needs the same step once: publish its
|
|
98
|
+
first version by hand before the first tag that holds it, or that tag's `npm stage publish` of it fails. After that no
|
|
99
|
+
token that publishes on its own exists anywhere: the job's identity stages, a maintainer's login needs its second
|
|
100
|
+
factor. Likewise once: make the three GHCR packages public after the first push, because GHCR creates them private.
|
|
100
101
|
- `publish.test.ts` asserts no registry, `access: public`, and `engines`; `dist-publish.sh` drops its manifest read-back
|
|
101
102
|
and passes `--registry`; the `@dist` fixture takes its registry from `JR2_DIST_PORT`'s default rather than the
|
|
102
103
|
manifest; `kit-push.sh`'s cli-vs-orchestrator cross-check widens to every manifest; `just release` is new;
|
|
103
104
|
`.github/workflows/` gains `ci.yml` and `release.yml`.
|
|
104
105
|
- A release costs a runner kind cluster and roughly ten minutes of docker. Accepted: it is the only time the installed
|
|
105
106
|
path must be proven on the exact tree that ships.
|
|
106
|
-
- The
|
|
107
|
+
- The public tarballs gain the npm-page fields (`description`, `repository` with `directory`, `homepage`, `bugs`,
|
|
107
108
|
`keywords`) and a README each; the root README's "only scaffolding exists" line goes.
|