@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
|
@@ -33,16 +33,21 @@ carved out for "the Orchestrator ending a run for its own reasons" was being spe
|
|
|
33
33
|
- **`stop()` stays public with no production caller, and is deliberately not on the wire.** It is correct, it is what
|
|
34
34
|
the durability suite drives, and it is what a future graceful shutdown should call. What nobody asked for is a
|
|
35
35
|
user-facing "park this run" verb.
|
|
36
|
+
- **A cancel of a standing run is also a pause.** The run ends `cancelled` as any run does, and jr2 does not start its
|
|
37
|
+
Workflow again until `jr2 run <workflow>` resumes it, with the items it held
|
|
38
|
+
([ADR-0017](0017-pool-over-a-source-port.md)). Without the pause, jr2 would start the standing run again at the next
|
|
39
|
+
boot or publish, and a person could not stop it. The runs that it dispatched go on, as for any dispatch.
|
|
36
40
|
- **A cancelled run's Workspaces are destroyed, unless the cancel says `keep`.** The run is over, so nothing can use
|
|
37
41
|
them, and each holds its whole Size (ADR-0060) — a placed pod left to the idle GC is capacity for nobody. The
|
|
38
42
|
wrapper's `teardown` is a STATE, entered when the body reaches final, and an actor stop skips it; so the host destroys
|
|
39
43
|
every Sandbox the run owns after the stop, the same idempotent delete `teardown` runs.
|
|
40
44
|
`POST /runs/:id/events { type: "CANCEL", keep: true }` — `jr2 send <run> --event CANCEL --keep` — leaves them for
|
|
41
|
-
inspection instead: with no Lease they age out at the operator's idle timeout, and `jr2
|
|
42
|
-
Whether to keep is a fact of the moment of the cancel, never a
|
|
43
|
-
before ([ADR-0012](0012-workspace-wrapper-machine.md)'s
|
|
44
|
-
chose, and the pod is the evidence. A **failed** run
|
|
45
|
-
|
|
45
|
+
inspection instead: with no Lease they age out at the operator's idle timeout, and `jr2 workspace shell` reaches them
|
|
46
|
+
until then; `jr2 workspace rm` removes one sooner. Whether to keep is a fact of the moment of the cancel, never a
|
|
47
|
+
config key. A **faulted** run keeps its Workspaces as before ([ADR-0012](0012-workspace-wrapper-machine.md)'s
|
|
48
|
+
destroy-less terminal, ADR-0021): a fault is the case nobody chose, and the pod is the evidence. A **failed** run
|
|
49
|
+
deletes them, as a `done` run does: a Failure is an end that the Machine chose
|
|
50
|
+
([ADR-0073](0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)).
|
|
46
51
|
|
|
47
52
|
## Considered options
|
|
48
53
|
|
package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md
CHANGED
|
@@ -57,8 +57,8 @@ does not exist for a caller that owns tool assembly. jr2 owns the rest: the engi
|
|
|
57
57
|
([ADR-0066](0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md)). The model sent
|
|
58
58
|
`bash` with `git add … && git commit …` and the pick `publish` in one model answer. In 3 of 10 trials, `publish`
|
|
59
59
|
pushed before the commit existed. A `write` and a `bash` test run in one model answer had the same race. The rule is
|
|
60
|
-
for all tools
|
|
61
|
-
the same time. Four facts follow:
|
|
60
|
+
for all tools: the Working tools, the MCP tools and the Menu. The cost is that independent reads of one model answer
|
|
61
|
+
do not run at the same time. Four facts follow:
|
|
62
62
|
- Only the first pick of a model answer runs
|
|
63
63
|
([ADR-0024](0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)). The model wrote a later one before it
|
|
64
64
|
read the first one's answer. The seam states two more facts, so that the Turn can find the first pick by itself. The
|
|
@@ -106,6 +106,13 @@ does not exist for a caller that owns tool assembly. jr2 owns the rest: the engi
|
|
|
106
106
|
no adapter: a test rig can pass another (`TurnDeps.engineAdapter`). So a second engine changes `engine-choice.ts`, not
|
|
107
107
|
the Turn. The Working tools, the printer, the transcript and Compaction do not change.
|
|
108
108
|
|
|
109
|
+
- **The Working tools run as confined children beside the engine.** The engine runs in the Harness process, which runs
|
|
110
|
+
jr2's code only and holds the control credential
|
|
111
|
+
([ADR-0076](0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md)). Each process that a
|
|
112
|
+
Working tool starts — each `bash` call and what it runs, and the search tools' `rg` or `grep` — runs confined by the
|
|
113
|
+
Effector's Landlock confiner ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)), with an
|
|
114
|
+
explicit env. The file tools (read, write, edit) run in the Harness process, and resolve each path beneath `/work`,
|
|
115
|
+
with no escape by `..` or by a symbolic link. A second engine keeps this rule: it is the Harness's, not pi's.
|
|
109
116
|
- **Assembly is runtime construction, not codegen.** `main.ts` constructs the server and listens; each admission carries
|
|
110
117
|
its Agent's definition (ADR-0049), validated loudly and re-read per Submission. `boot.mjs`, the generated shims, the
|
|
111
118
|
boot-time `flue build`, and the readiness lag it caused are deleted, and so — later — was the `JR2_AGENTS_JSON` roster
|
|
@@ -170,10 +177,11 @@ does not exist for a caller that owns tool assembly. jr2 owns the rest: the engi
|
|
|
170
177
|
long as its run** (ADR-0031). The Harness persists each conversation to a directory as it goes and rebuilds it on
|
|
171
178
|
boot, so a Harness restart inside a living pod keeps it; a Submission in flight at the restart settles `failed`.
|
|
172
179
|
What is durable across a lost pod is Orchestrator-side — the admission ledger and snapshot
|
|
173
|
-
([ADR-0007](0007-durable-machine-state.md)),
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
`
|
|
180
|
+
([ADR-0007](0007-durable-machine-state.md)), `wait`'s reconnect-from-offset, and the kept conversation, which a
|
|
181
|
+
person reads and no Turn continues from ([ADR-0023](0023-the-harness-prints-the-conversation.md)). A conversation
|
|
182
|
+
that is gone surfaces as 404 → fault → workflow policy, the same family as `workspace.lost`. Provider-stream retry
|
|
183
|
+
moves inside the turn: the Harness states 2 retries, and pi-ai runs them (`maxRetries`) — the seat ADR-0016
|
|
184
|
+
delegated to flue's `durability{}`.
|
|
177
185
|
- **[ADR-0013](0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md)'s enabling fact is reproduced
|
|
178
186
|
as explicit code.** Each Submission reads `$JR2_CUSTODIAN_URL/agents/<iid>/surface` afresh and presents it to pi as
|
|
179
187
|
the Menu, so the menu is per-turn with no push channel, no turn index, no second port. Menu tools keep the
|
|
@@ -219,7 +227,8 @@ does not exist for a caller that owns tool assembly. jr2 owns the rest: the engi
|
|
|
219
227
|
- **Kept verbatim**: the wire, the stub as the mechanics fixture, `AgentRunPort` and the admission ledger, ADR-0024's
|
|
220
228
|
rule, receipt, and ordering, and the container split — restated in jr2 terms: the pod's credentials live in a separate
|
|
221
229
|
container (the Custodian, ADR-0059) _because working tools execute in the Harness container_, so the
|
|
222
|
-
credential-isolation premise survives while `local()` leaves the vocabulary.
|
|
230
|
+
credential-isolation premise survives while `local()` leaves the vocabulary. The one credential that the Harness
|
|
231
|
+
process holds, the control Stand-in, is out of reach of the Working tools' children, which run confined (ADR-0076).
|
|
223
232
|
- **The `flue-contract` tier dissolves into the conformance suite.** Its rig — scripted OpenAI-compatible provider, the
|
|
224
233
|
Menu over a real socket to a killable fake Orchestrator, real server — moves into `packages/harness/test/` and runs in
|
|
225
234
|
the default `test` gate: the opt-in-ness existed only because the tier owned a foreign pin and build. Its claims
|
|
@@ -13,8 +13,8 @@ instructions that say "Do not modify the code yourself".
|
|
|
13
13
|
[ADR-0024](0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md) already established what that means: the same
|
|
14
14
|
prompt, model, and state produce compliance on one run and not the next, "which is what rules out fixing this with
|
|
15
15
|
words." The prose ban is a persona, not a boundary. ADR-0024 also named the stakes — an Agent with write access is a
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
writer that the Machine did not plan for — and [ADR-0012](0012-workspace-wrapper-machine.md) puts coder and reviewer in
|
|
17
|
+
one Workspace on purpose.
|
|
18
18
|
|
|
19
19
|
Under the retired flue runtime this had no seat — its tool list was take-it-or-replace-it, and the only recorded out was
|
|
20
20
|
ejecting to a foreign harness project. [ADR-0027](0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md)
|
|
@@ -31,9 +31,12 @@ definition can state.
|
|
|
31
31
|
Workspace (it is this ADR's title). A string field, JSON-serializable, riding the existing ConfigMap channel unchanged
|
|
32
32
|
(ADR-0018's constraint holds).
|
|
33
33
|
- **`"none"` is the Menu-only Agent — and the value also names the Harness that hosts the Agent.** `"none"` withholds
|
|
34
|
-
the entire Working toolset: the Agent converses
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
the entire Working toolset: the Agent converses, picks from its Menu and calls the MCP tools of the remote servers it
|
|
35
|
+
names
|
|
36
|
+
([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)), and
|
|
37
|
+
runs no code — the decisioning persona (read inputs, pick the next event). "Menu-only" means "no Working tools": such
|
|
38
|
+
an Agent can also hold MCP tools. Because such an Agent touches no Workspace, it does not run in one:
|
|
39
|
+
`"write"`/`"read"` place a Turn on the enclosing Workspace's Harness; `"none"` places it on the
|
|
37
40
|
[Instance Harness](0031-menu-only-agents-run-on-the-instance-harness.md), always — even when a `workspace()` encloses
|
|
38
41
|
the invocation. Placement is definitional, which is this ADR's thesis extended one step: what an Agent may do to the
|
|
39
42
|
Workspace decides where it runs. (`cwd` is moot for `"none"` — only Working tools consume it.)
|
|
@@ -41,19 +44,32 @@ definition can state.
|
|
|
41
44
|
tools (the tool set that the Harness gives the loop for each Submission). `bash` stays — the reviewer's own
|
|
42
45
|
instructions require running the tests — and the ADR says plainly what that means: a shell can write, so the tool
|
|
43
46
|
layer states intent and stops the honest path. It is known leaky, and it is not the guarantee.
|
|
44
|
-
- **The worktree layer contains the blast radius: a reviewer works in a detached review
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
— it is containment, the property ADR-0024 bought for
|
|
47
|
+
- **The worktree layer contains the blast radius: a reviewer works in a detached review Worktree.** The Machine cuts it
|
|
48
|
+
with `worktree()`, `{ path, detach: sha }`, at the sha under review, with a detached HEAD, sibling to the coder's
|
|
49
|
+
Worktree per the Project layout ([ADR-0075](0075-a-machine-cuts-a-worktree-and-the-workspace-only-clones.md)). A write
|
|
50
|
+
there cannot move the branch or dirty the coder's Worktree; a rogue commit lands on a detached HEAD and goes when the
|
|
51
|
+
next cut forces the folder. This is not a permission boundary — it is containment, the property ADR-0024 bought for
|
|
52
|
+
turns, bought for the filesystem.
|
|
49
53
|
- **Two layers, one structure: the tool list is the hint, the worktree is the guarantee** — the same shape as ADR-0024's
|
|
50
54
|
receipt (hint) and abort (guarantee). Neither layer alone: tools without the worktree leak through bash; the worktree
|
|
51
55
|
without tools invites the model to fight its checkout instead of declining politely.
|
|
52
|
-
- **
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
- **An MCP allowlist is a guarantee about the credential, and jr2 offers it only where it can keep it.** An Agent's MCP
|
|
57
|
+
tools ([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md))
|
|
58
|
+
spend the server's token, so the allowlist guards that token, not the Workspace. A remote server needs a list (tool
|
|
59
|
+
names, or `"*"`): the Harness offers and runs only those tools. Where Workspace code of the Sandbox in which the
|
|
60
|
+
Agent's Turns run can spend that secret, Workspace code can call every tool, so `jr2 up` refuses a list and takes only
|
|
61
|
+
`"*"`. That is the case when the Sandbox's `workspace()` grants the secret, when a credentialed `command` of that
|
|
62
|
+
Workspace names it, or when an `http` Effect of that Workspace whose body is a Worktree file names it: the Agent
|
|
63
|
+
writes that body ([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)). A `"none"` Agent's
|
|
64
|
+
Turns run on the Instance Harness, where no Workspace code runs, so it keeps its list. A stdio server runs as
|
|
65
|
+
Workspace code and gets no list. The token's own scopes are the outer limit. This is not the shape of
|
|
66
|
+
`workspace: "read"` above: there the tool list is the hint, because `bash` can write; here the list is the guarantee,
|
|
67
|
+
because no Workspace code can spend the secret.
|
|
68
|
+
- **A state before the reviewing state cuts the review Worktree on each round.** The sha under review moves between
|
|
69
|
+
rounds, so each round cuts the one folder again at the new sha. The cut forces the folder to the commit and cleans it
|
|
70
|
+
with `-ffd`. Ignored files stay, because the reviewer runs the tests. The folder's path frames the reviewer's turn —
|
|
71
|
+
cwd and prompt — instead of the coder's Worktree. What is decided here is the seat: worktree mechanics stay in
|
|
72
|
+
`worktree()` and the Machine's states, never in Agent instructions.
|
|
57
73
|
|
|
58
74
|
## Considered options
|
|
59
75
|
|
|
@@ -78,10 +94,11 @@ definition can state.
|
|
|
78
94
|
- **The reviewer definition becomes `workspace: "read"`**, and the prose ban stays in its instructions as intent — the
|
|
79
95
|
half of the contract a model reads — no longer as the mechanism. A decisioner becomes `workspace: "none"`.
|
|
80
96
|
- **The definition stays plain data.** No code enters the ConfigMap channel; composition by re-export/spread (ADR-0018)
|
|
81
|
-
is preserved.
|
|
82
|
-
|
|
83
|
-
- **GLOSSARY.md arbitrates the
|
|
84
|
-
|
|
97
|
+
is preserved. Tool _implementations_ stay out of the contract — this decision adds a restriction vocabulary, not an
|
|
98
|
+
extension one. The extension is MCP: an Agent names servers, and a server holds the code (ADR-0079).
|
|
99
|
+
- **GLOSSARY.md arbitrates the senses of "tools"**: the Menu (control plane — what an Agent may say), Working tools
|
|
100
|
+
(data plane — what it may do) and MCP tools (what it may do through a service, ADR-0079) are glossary entries;
|
|
101
|
+
unqualified "tools" joins the Avoid lists.
|
|
85
102
|
- **The `@kind` tier gains the containment scenario**: from the review worktree, a write probe followed by a commit must
|
|
86
103
|
leave the branch ref and the coder's worktree untouched.
|
|
87
104
|
- **The zero-support guard stays available to workflows**: a workflow can still have both Agents report the sha and gate
|
|
@@ -21,12 +21,19 @@ whole history again. It also left the history calling tools the request no longe
|
|
|
21
21
|
|
|
22
22
|
## Decision
|
|
23
23
|
|
|
24
|
-
- **The Menu is the whole of what an Agent can ever be offered in its Machine, and it is the
|
|
25
|
-
union of the derived sets of every state that invokes an Agent of that name — the same scope as its
|
|
26
|
-
conversation's id (ADR-0057). `jr2Setup.createMachine` derives it in ADR-0015's walk, in a fixed order, so
|
|
27
|
-
byte-stable for every Turn of the conversation and for every fresh conversation of that Agent. The descriptions
|
|
28
|
-
the event defs' own and do not vary by state, so a def's description must hold at every writing of it the Agent
|
|
29
|
-
reach; the load warns where its writings on one path differ (ADR-0015).
|
|
24
|
+
- **The Menu is the whole of what an Agent can ever be offered in its Machine, and it is the Menu's part of the tools
|
|
25
|
+
block.** It is the union of the derived sets of every state that invokes an Agent of that name — the same scope as its
|
|
26
|
+
continued conversation's id (ADR-0057). `jr2Setup.createMachine` derives it in ADR-0015's walk, in a fixed order, so
|
|
27
|
+
it is byte-stable for every Turn of the conversation and for every fresh conversation of that Agent. The descriptions
|
|
28
|
+
are the event defs' own and do not vary by state, so a def's description must hold at every writing of it the Agent
|
|
29
|
+
can reach; the load warns where its writings on one path differ (ADR-0015).
|
|
30
|
+
- **An Agent's MCP tools are fixed for the conversation too.** The Harness reads a server's tools at the conversation's
|
|
31
|
+
first Turn, keeps the allowed ones sorted by name, records them with the conversation, and never reads them again for
|
|
32
|
+
it, across a park and a restart
|
|
33
|
+
([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)). The
|
|
34
|
+
tools block is the Working tools, then the MCP tools, then the Menu, and it is the same bytes for every Turn of the
|
|
35
|
+
conversation. Any fixed order keeps the cache. A tool that the server took away stays in the block, and its call fails
|
|
36
|
+
with jr2's text.
|
|
30
37
|
- **What one Turn may pick is its Allowed picks**: the invoking state's derived set (its own, its ancestors' and its
|
|
31
38
|
modes' picks — ADR-0015), narrowed to what the live Machine would currently accept **from this Agent**: xstate's
|
|
32
39
|
selection for an event that carries the Agent's sender, a guard that reads the payload counted as passing, must move
|
|
@@ -117,7 +124,8 @@ block was not perfect either: once the model invented a tool it had never been g
|
|
|
117
124
|
cross-state `continue`.
|
|
118
125
|
- **The Machine's whole agent-audience vocabulary as the Menu.** Rejected: it offers picks only another Agent's states
|
|
119
126
|
handle.
|
|
120
|
-
- **
|
|
127
|
+
- **Narrow the Menu per Turn, with the Menu tools last so that only the tail of the block changes.** Rejected: measured,
|
|
128
|
+
a change at the tail of the tools block misses as completely as one at its head.
|
|
121
129
|
- **Mask at decode time** (OpenAI `allowed_tools`, logit masking). Deferred: it is provider-specific — Anthropic has no
|
|
122
130
|
subset form, and changing `tool_choice` there drops the messages cache; vLLM's Chat Completions refuses it. The design
|
|
123
131
|
must hold without it; where it is cache-safe, it can be added in the presenter.
|
|
@@ -141,9 +149,9 @@ block was not perfect either: once the model invented a tool it had never been g
|
|
|
141
149
|
|
|
142
150
|
## Consequences
|
|
143
151
|
|
|
144
|
-
- **One state can allow different picks as its context changes or its active mode changes; the Menu
|
|
145
|
-
The conformance tier asserts it: two Submissions with different Allowed picks send a byte-identical
|
|
146
|
-
(ADR-0027).
|
|
152
|
+
- **One state can allow different picks as its context changes or its active mode changes; the Menu and the MCP tools do
|
|
153
|
+
not change.** The conformance tier asserts it: two Submissions with different Allowed picks send a byte-identical
|
|
154
|
+
tools block (ADR-0027).
|
|
147
155
|
- **A restatement is appended, never rewritten.** Conformance asserts that each request of a Submission is a byte prefix
|
|
148
156
|
of the next, with the tools and the system prompt unchanged, and that the restatement sits right after the model
|
|
149
157
|
answer's last tool result.
|
|
@@ -89,8 +89,9 @@ to ask about.
|
|
|
89
89
|
([ADR-0070](0070-a-machine-lends-its-parts-to-a-chart.md)) is the run's data, not its Machine's shape: the Chart's
|
|
90
90
|
text and its hash ride the body's own snapshot, restore admits the text again and compares the hash, and a mismatch or
|
|
91
91
|
a refusal is drift naming the body slot and the reason, after the stamp check and in the same words.
|
|
92
|
-
- **A drifted run is
|
|
93
|
-
and `jr2 status <id
|
|
92
|
+
- **A drifted run is in `jr2 runs --status drifted`** and in the Console's list of ended runs, with its reason
|
|
93
|
+
([ADR-0009](0009-cli-and-instance-interface.md)), and `jr2 status <id>` reads it whole. It is not in the default
|
|
94
|
+
listing, which is the live runs.
|
|
94
95
|
- **The digest is only as complete as the walk.** A state running `enqueueActions` can spawn children no static analysis
|
|
95
96
|
recovers (`machine-doc.ts`'s `opaqueStates`); the opacity is folded into the hash, but what it hides is not. Drift
|
|
96
97
|
inside such a subtree can go unnoticed.
|
|
@@ -39,16 +39,23 @@ built.
|
|
|
39
39
|
the Harness has exactly one route to its Menu — the Custodian on `localhost` — and forking that path for one pod buys
|
|
40
40
|
a divergence in the component this ADR keeps deliberately uniform. The pod is composed by the same function a
|
|
41
41
|
Sandbox's is ([ADR-0059](0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md)), so its Custodian
|
|
42
|
-
carries the Instance Harness's model calls the same way. It holds
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
carries the Instance Harness's model calls the same way. It holds what the `"none"` Agents it hosts name — the keys
|
|
43
|
+
their providers read, and the secrets of the remote MCP servers they name
|
|
44
|
+
([ADR-0079](0079-an-agent-holds-mcp-tools-a-machine-calls-them-as-an-effect-and-the-machine-is-the-approval.md)) — and
|
|
45
|
+
none of the held secrets a `workspace()` lists: a `"none"` Agent cannot execute code, so it had no way to use the
|
|
46
|
+
rest. A `"none"` Agent names no stdio MCP server, because the Instance Harness has no Landlock domain to start one in.
|
|
47
|
+
Its held objects are its own (`held-instance.json`, `jr2-held-secrets-instance`). Its held secrets get secret
|
|
48
|
+
Stand-ins, as in every Harness pod: `jr2 up` mints them with the signing key when it writes that pod's Secrets, as it
|
|
49
|
+
does for the Instance Effector's key ([ADR-0059](0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md)).
|
|
50
|
+
It starts no confined child, so this is for consistency, not a boundary. Defense-in-depth is the bonus, not the
|
|
51
|
+
reason.
|
|
46
52
|
- **The Instance Effector is the second Instance-level pod, beside it**
|
|
47
53
|
([ADR-0068](0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md)), on the same convention: `jr2 up`
|
|
48
|
-
converges it when a registered Machine places an Effect there — an `http
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
converges it when a registered Machine places an Effect there — an `http`, an `mcp` or a targeted `dispatch` Effect —
|
|
55
|
+
or a Hook needs it (a `mac` verifier, or a kind that declares `posts`), and deletes it otherwise. It runs the same
|
|
56
|
+
`jr2-harness` image with `/opt/jr2/bin/effector` as its command, holds no conversation and runs no Agent, and is a
|
|
57
|
+
Deployment, not a StatefulSet: it keeps nothing, and a rollout must run the new pod beside the old one. No config key
|
|
58
|
+
names, sizes, addresses, or enables it.
|
|
52
59
|
- **No image config at all.** The Harness image was once configured as `sandbox.image` because the Sandbox pod was the
|
|
53
60
|
only place a Harness ran — a misnomer once the Instance Harness exists, and config is the wrong seat regardless:
|
|
54
61
|
`jr2 up` builds and resolves every image it deploys ([ADR-0038](0038-jr2-up-builds-every-image-it-deploys.md)), and
|
|
@@ -68,13 +75,14 @@ pointers only; memory snapshots are tied to one template version, so the state w
|
|
|
68
75
|
change is a volume the actor reloads on a cold boot. These decisions take that shape.
|
|
69
76
|
|
|
70
77
|
- **A conversation is persisted to a directory, and rebuilt from it on boot.** The Harness writes each conversation as
|
|
71
|
-
it goes: jr2's own record — the stream log, the Settlements, the history messages,
|
|
72
|
-
engine part, in a folder the engine names (`engine/<name>`), which the record does not
|
|
73
|
-
transcript (ADR-0027): `engine/pi/transcript.jsonl`, the pi-ai messages the model read and
|
|
74
|
-
reads jr2's record first; an engine that cannot read the engine part (another engine,
|
|
75
|
-
the history messages. The record is the contract; the engine part is not. A Submission
|
|
76
|
-
settles `failed` (the typed error `harness_restarted`, which the Agent's Actor reads as "Turn
|
|
77
|
-
stream, so the Orchestrator reads a Settlement, not a 404, and the conversation's next Turn
|
|
78
|
+
it goes: jr2's own record — the stream log, the Settlements, the history messages, the conversation's MCP tools, in
|
|
79
|
+
the wire's shapes — plus the engine part, in a folder the engine names (`engine/<name>`), which the record does not
|
|
80
|
+
read. Today it is jr2's transcript (ADR-0027): `engine/pi/transcript.jsonl`, the pi-ai messages the model read and
|
|
81
|
+
each Compaction. Rebuilding reads jr2's record first; an engine that cannot read the engine part (another engine,
|
|
82
|
+
another version) continues from the history messages. The record is the contract; the engine part is not. A Submission
|
|
83
|
+
in flight at the restart settles `failed` (the typed error `harness_restarted`, which the Agent's Actor reads as "Turn
|
|
84
|
+
lost") on the rebuilt stream, so the Orchestrator reads a Settlement, not a 404, and the conversation's next Turn
|
|
85
|
+
continues it.
|
|
78
86
|
- **A restart while the Agent's state is busy cuts the Turn and is no fault.** When the Agent's state is not at Rest —
|
|
79
87
|
its pick is outstanding, or work it started in runs below it — or its pick's answer was read and never reached the
|
|
80
88
|
model, the Agent's Actor raises no `agent.fault` for a restart: the Machine would route it and stop the work the Turn
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# The Console unlocks with a Caller's credential, and shows what its scopes allow
|
|
2
|
+
|
|
3
|
+
[ADR-0014](0014-observation-is-open-run-state-is-not.md) built the HTTP surface on one premise: the visualizer page is a
|
|
4
|
+
browser, and a browser holds no token — so it got a projection instead of a credential, and "give the page a token" was
|
|
5
|
+
explicitly rejected. That page is now the **Console**: it starts runs, answers Gates and cancels runs, which are writes
|
|
6
|
+
on the guarded surface. This ADR records how a browser earns those writes without moving the open band. The person
|
|
7
|
+
presents a credential; the page never ships with one.
|
|
8
|
+
|
|
9
|
+
## Decision
|
|
10
|
+
|
|
11
|
+
- **The person presents a Caller's credential; the page never ships with one.** The Console's nav carries a password
|
|
12
|
+
field, and a login button for each JWT issuer that the Instance declares with `login: true`
|
|
13
|
+
([ADR-0078](0078-a-caller-comes-from-one-authenticator-seam-and-acts-within-its-scopes.md)). Empty, the page is
|
|
14
|
+
precisely ADR-0014's observer: open structure and observation, no control rendered. With a credential, the page sends
|
|
15
|
+
`Authorization: Bearer` on the guarded surface. The credential is any Caller's: the Instance token, a named Caller
|
|
16
|
+
token, or a JWT. The login gets a JWT from the issuer with OIDC (the authorization code flow with PKCE, as a public
|
|
17
|
+
client), and the page then uses it as it uses a typed token. The page as _served_ stays open and inert; ADR-0014's
|
|
18
|
+
rejection stands, because what it rejected was baking the credential into the page.
|
|
19
|
+
- **The login's facts come from an open route, and its client id is the issuer's `audience`.** Before a login, the page
|
|
20
|
+
reads `GET /console/config`: `{ login: [{ issuer, clientId }] }`, from the issuers of the current Assembly that have
|
|
21
|
+
`login: true`. The route is in the open band (ADR-0014), because the page reads it before it holds a credential, and
|
|
22
|
+
both values are public: a public client has no secret, and the issuer's URL is in every JWT that it signs. The client
|
|
23
|
+
id is the issuer entry's `audience`, because the JWT that the login gives names its client as its audience, and that
|
|
24
|
+
is what the JWT authenticator checks. A login is opt-in for each issuer, because an issuer for machines (a CI
|
|
25
|
+
provider's tokens) has no login page. The page reads the issuer's discovery document, asks for `openid`, `profile`,
|
|
26
|
+
`email` and `groups` where the issuer lists each, and sends the ID token as the bearer: an access token's audience and
|
|
27
|
+
form differ between providers. The return address is always the Console's root (`/`), so the client registers one
|
|
28
|
+
redirect URI; the page then puts the address back to where the login began. The verifier, the state and that address
|
|
29
|
+
wait in `sessionStorage` until the return, and a return whose state does not match is refused. PKCE needs
|
|
30
|
+
`crypto.subtle`, so a login needs the Console on https or on localhost, and the issuer must let the Console's origin
|
|
31
|
+
call its discovery document and its token endpoint (CORS).
|
|
32
|
+
- **The page shows only what the Caller's scopes allow.** On entry the page asks `GET /whoami` for the Caller record:
|
|
33
|
+
its name, the name that jr2 shows, and its scopes for each Workflow. It renders a control only where a scope opens its
|
|
34
|
+
route: the start form with `start` for that Workflow, Gate cards with `answer`, cancel with `cancel`, a run's full
|
|
35
|
+
state with `read`, and the Steering box with `steer`. The page re-fetches a run on a frame only for a Caller with
|
|
36
|
+
`answer` or `read` on its Workflow. The nav shows who the page acts as: the name that jr2 shows, else `<via>/<name>`,
|
|
37
|
+
with the scopes in its title, and a sign-out button in place of the field. The server still checks each request
|
|
38
|
+
(ADR-0014); the page hides a control so that a person does not meet a 403, not as a guard.
|
|
39
|
+
- **The credential lives in `sessionStorage`** — survives a reload, dies with the tab, never a cookie (no ambient
|
|
40
|
+
credential, no CSRF surface). A typed token and a login's JWT live there alike. A later 401 (an expired JWT, a rotated
|
|
41
|
+
token) drops the UI back to observer mode rather than failing per widget.
|
|
42
|
+
- **Gate discovery is a frame-triggered guarded read, not an open-band extension.** A Gate exists exactly while its
|
|
43
|
+
invoking state is entered ([ADR-0011](0011-workflow-defined-events.md)), and every entry/exit lands a status frame on
|
|
44
|
+
the open workflow feed (ADR-0022) — so a Console whose Caller has `answer` reads `GET /gates` on entry and again on
|
|
45
|
+
each frame, and folds the Gates into its store. `GET /gates` lists only the Gates that the Caller may answer, across
|
|
46
|
+
every Workflow (ADR-0078), so the Gate cards are the person's inbox and never show a Gate the person cannot answer.
|
|
47
|
+
Synchronized, no polling loop, and the observation projection is untouched. A delivery's success is likewise confirmed
|
|
48
|
+
by the next frame emptying the card, never by local bookkeeping.
|
|
49
|
+
- **The rail lists ended runs too.** It lists the live runs and, under them, the ended runs of the selected Workflow
|
|
50
|
+
from `GET /runs?status=ended&workflow=<name>` (ADR-0009), each with its status, and a `drifted` run with its reason
|
|
51
|
+
(ADR-0030). An ended run opens as a live one does: its graph, its last status and its conversations, for as long as
|
|
52
|
+
jr2 keeps the run (ADR-0065). A run on an older Assembly carries the mark "older Assembly · <hash>" and is drawn on
|
|
53
|
+
its own Machine, live or ended: an ended run's row names the Machine it ran on
|
|
54
|
+
([ADR-0069](0069-the-orchestrator-is-a-kit-image-and-an-instance-publishes-its-assembly.md)). A `drifted` run has no
|
|
55
|
+
Machine to draw, so it shows its drift reason in place of the graph. The Console shows each outstanding pick as
|
|
56
|
+
"outstanding since".
|
|
57
|
+
- **A conversation opens in a bottom drawer.** A full-width drawer across the bottom of the page shows the run's
|
|
58
|
+
conversations ([ADR-0023](0023-the-harness-prints-the-conversation.md)). A person can change its height, and the page
|
|
59
|
+
keeps the height for that viewer. Closed, it is a bar of tabs, one for each Agent slot, with a dot on a slot whose
|
|
60
|
+
Turn is live. Open, it shows the selected slot's conversation and follows a live Turn, with the Steering box beside it
|
|
61
|
+
for a Caller with `steer`. It reads the conversation routes, so it needs `read`. The right drawer keeps Attention and
|
|
62
|
+
Activity.
|
|
63
|
+
- **Page addresses content-negotiate; JSON is the default dialect.** `/` and `/workflows/:name` serve the Console shell
|
|
64
|
+
to `Accept: text/html` and JSON to everything else — so the workflow-detail resource keeps its natural address (the
|
|
65
|
+
run-input schema's home, [ADR-0033](0033-a-machine-declares-the-input-that-starts-it.md)) and no future page has to
|
|
66
|
+
squat on a mangled sibling path. Nothing existing moves: EventSource asks `text/event-stream`, and the Harness's Menu
|
|
67
|
+
and the CLI never ask for HTML. `/viz/*` is deleted (greenfield, no redirects); assets move to `/assets/*`, which also
|
|
68
|
+
retires the "a workflow named `assets` is shadowed" hazard.
|
|
69
|
+
|
|
70
|
+
## Considered options
|
|
71
|
+
|
|
72
|
+
- **`jr2 ui` — the CLI opens the browser through an authenticated local proxy.** A convenience layer worth building _on
|
|
73
|
+
top of_ the token field later, not a substitute: it has no answer for the deployed jr2 Application behind an ingress,
|
|
74
|
+
where the browser talks to the Service directly and the CLI is not in the path.
|
|
75
|
+
- **The Instance token alone.** Rejected (ADR-0078): a person who must answer Gates and holds no cluster RBAC would then
|
|
76
|
+
hold every act on every run. A scoped Caller's credential is what that person holds.
|
|
77
|
+
- **The conversation in the right drawer.** Rejected: a conversation needs the page's width, and the right drawer keeps
|
|
78
|
+
Attention and Activity.
|
|
79
|
+
- **Announce gates on the open observation feed** (even just `{runId, gate}`). Rejected: it widens the projection for a
|
|
80
|
+
convenience the credential already solves, and the open band has an unresolved exposure question as it stands (spawn
|
|
81
|
+
ids — the ADR-0014 open call). The projection stays the one place the line is drawn.
|
|
82
|
+
- **A `/ui` path prefix, or moving the API under `/api`.** Rejected for negotiation: the first is ceremony on the
|
|
83
|
+
address a human types, the second churns the Harness's Menu, the Custodian's route allowlist, the CLI, and the
|
|
84
|
+
ADR-0009/0013 route tables for cosmetics.
|
|
85
|
+
|
|
86
|
+
## Consequences
|
|
87
|
+
|
|
88
|
+
- **No new band.** The server-side diff of the Console itself is: negotiate two addresses, serve the shell, retire
|
|
89
|
+
`/viz/*`, and serve `GET /console/config` in the open band. The scopes, `GET /whoami` and the login's issuers are
|
|
90
|
+
ADR-0078's.
|
|
91
|
+
- **Possession follows the credential.** The Instance token stays one `kubectl get secret` away for whoever holds
|
|
92
|
+
cluster RBAC; a named token is what its holder was given; a login is what the identity provider's groups grant.
|
|
93
|
+
- **The XSS caveat is the real one and is bounded by what the page is:** a credential in `sessionStorage` is readable by
|
|
94
|
+
scripts on the page, so the Console must keep serving only its own assets — no CDN, no third-party script, exactly the
|
|
95
|
+
constraint the viz page already lives under.
|
|
96
|
+
- **GLOSSARY.md**: **Console** is a glossary term (visualizer, dashboard, UI, viz on its Avoid list).
|
|
97
|
+
- **The Console's UX gets a browser tier** — `@console`-tagged Cucumber scenarios holding a Playwright page, in
|
|
98
|
+
`@jr2/e2e`, excluded from the default profile ([ADR-0010](0010-bdd-acceptance-tests.md) as amended). What the page
|
|
99
|
+
_believes_ stays unit-tested in `console-store.test.ts`, and the login's pure half in `console-login.test.ts`; the
|
|
100
|
+
browser tier asserts what a user does and sees, and the login against Dex in a container (ADR-0078). The login
|
|
101
|
+
scenario is also tagged `@dex`, because it needs docker, which the rest of the tier does not.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
`POST /workflows/:name/runs` takes a JSON body and hands it to the machine as xstate `input` — unvalidated, and
|
|
4
4
|
undescribed: the module contract is `export const machine` alone (ADR-0011/0015), so nothing at runtime says what a run
|
|
5
5
|
of a given workflow should be started _with_. The Console's start form
|
|
6
|
-
([ADR-0032](0032-the-console-unlocks-with-
|
|
6
|
+
([ADR-0032](0032-the-console-unlocks-with-a-callers-credential.md)) forced the question, but the gap predates it —
|
|
7
7
|
`jr2 run` has the same blindness, and a typo'd input surfaces as a confusing mid-run failure instead of a refusal at the
|
|
8
8
|
door. Meanwhile every event a machine _accepts_ already carries a zod schema, attached to the machine itself
|
|
9
9
|
([ADR-0015](0015-authoring-surface-absorbs-the-mechanism.md)'s vocabulary-on-the-machine). The input that starts a
|
|
@@ -24,9 +24,9 @@ machine is the one piece of its vocabulary that pattern missed.
|
|
|
24
24
|
Thus a `workspace()` or `pool()` wrapper with its own door passes. A `.provide()` clone keeps the config of its
|
|
25
25
|
original, so the walk gets the same result for the clone. A Machine with no `input`, or with an `input` that is not
|
|
26
26
|
zod, can use `setup()`. The `ping` Workflow that `jr2 init` writes uses it. The walk checks the registered roots only,
|
|
27
|
-
because
|
|
28
|
-
when you call it. It reads the config and the attached door, so it also refuses a body that
|
|
29
|
-
and rejected:
|
|
27
|
+
because a run starts at a root, and a door under a parent is parsed at invoke (see below). `workspace()` refuses a
|
|
28
|
+
body with a zod `input` when you call it. It reads the config and the attached door, so it also refuses a body that
|
|
29
|
+
`setup()` made. Considered and rejected:
|
|
30
30
|
- Refuse all `setup()` Machines. This adds work to the first Workflow and gives nothing.
|
|
31
31
|
- Refuse at boot. Then an Assembly that is already published stops the Orchestrator. The walk refuses only the next
|
|
32
32
|
publish.
|
|
@@ -34,7 +34,7 @@ machine is the one piece of its vocabulary that pattern missed.
|
|
|
34
34
|
two ways to declare one door. Also, the zod schema stays in `machine.config`, and a schema must not go into the
|
|
35
35
|
fingerprint and serialization paths that read the config. And that Machine does not get the Vocabulary checks of
|
|
36
36
|
`jr2Setup`.
|
|
37
|
-
- **A wrapper declares its own door; it never borrows its child's.** `workspace(body, { input
|
|
37
|
+
- **A wrapper declares its own door; it never borrows its child's.** `workspace(body, { input })` and
|
|
38
38
|
`pool(worker, { input, … })` each take an `input` of their own, and neither propagates one upward. The reason is the
|
|
39
39
|
same in both, at different strengths: a wrapper does not feed its child what a caller sent. `pool` is the sharp case —
|
|
40
40
|
a worker is fed per-item, never the run body, so propagating its schema would demand fields the pool never passes and
|
|
@@ -55,10 +55,10 @@ machine is the one piece of its vocabulary that pattern missed.
|
|
|
55
55
|
it was rejected because the author then chooses between two names for one statement, and finds the second only after
|
|
56
56
|
the opaque error. A body that _still_ declares a schema is refused by `workspace()` with a message naming the fix:
|
|
57
57
|
nothing would ever serve or validate it, and a contract nobody enforces is worse than none.
|
|
58
|
-
- **The door types the mapping, and constrains the body.**
|
|
59
|
-
schema (`z.infer`), so a workflow states its shape once and the workspace mapping is checked against it — the
|
|
60
|
-
this ADR exists to close, closed in the one place that used to re-declare the shape by hand. The same schema
|
|
61
|
-
the _body_, in **one direction only**: the body may not demand more than the wrapper will hand it, which is
|
|
58
|
+
- **The door types the mapping, and constrains the body.** A per-run Repo Slot's mapper reads its `input` from the
|
|
59
|
+
declared schema (`z.infer`), so a workflow states its shape once and the workspace mapping is checked against it — the
|
|
60
|
+
drift this ADR exists to close, closed in the one place that used to re-declare the shape by hand. The same schema
|
|
61
|
+
checks the _body_, in **one direction only**: the body may not demand more than the wrapper will hand it, which is
|
|
62
62
|
`Workspaced<door, slots>`; demanding _less_ is safe, because it is fed a superset. So a body reading a field the door
|
|
63
63
|
never carries is a build error at the `workspace()` call instead of an `undefined` mid-run. The guard counts the raw
|
|
64
64
|
door as provided beside `Workspaced<door, slots>`: for the door `z.object({})` the two differ, and a body that writes
|
|
@@ -75,27 +75,27 @@ machine is the one piece of its vocabulary that pattern missed.
|
|
|
75
75
|
a parent invoking a nested `workspace()` has its input mapper checked against that wrapper's door, and gets the body's
|
|
76
76
|
output typed back. With no schema declared the door is permissive, so there is nothing to infer and the mapper's
|
|
77
77
|
argument is `unknown` — an honest "jr2 does not know", not `any`; a wrapper fed by something other than a caller (a
|
|
78
|
-
pool worker) states what it is fed by annotating the parameter, as `PoolSpec.cap`/`itemInput` do.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
`
|
|
82
|
-
|
|
83
|
-
For the door `z.object({})` that input is `{}`, the door without its `Record<string, never>`, so a parent can give
|
|
84
|
-
`instanceId` to a nested `spec` that reads it; with the raw `z.infer` it could not, and the nested Workspace then
|
|
85
|
-
pushed each run to `fix/undefined`. `z.infer<…> & Partial<HostInjectedInput>` was the alternative; it was rejected
|
|
86
|
-
because the index signature makes `instanceId` `never` there too. A per-run Repo Slot's mapper keeps the door alone,
|
|
87
|
-
because a url does not come from a run's id.
|
|
78
|
+
pool worker) states what it is fed by annotating the parameter, as `PoolSpec.cap`/`itemInput` do. The input of the
|
|
79
|
+
wrapper stays the door alone, so no parent must send `instanceId`, and a nested body that reads it needs a parent that
|
|
80
|
+
gives it. For the door `z.object({})` that input is `{}`, the door without its `Record<string, never>`, so a parent
|
|
81
|
+
can give `instanceId` to such a body; with the raw `z.infer` it could not. A per-run Repo Slot's mapper keeps the door
|
|
82
|
+
alone, because a url does not come from a run's id.
|
|
88
83
|
- **The schema is structure, so it is open.** Workflow-detail JSON (`GET /workflows/:name`, the address ADR-0032's
|
|
89
84
|
negotiation kept) serves it as JSON Schema — same band as the Machine doc, and what drives the Console's start form
|
|
90
85
|
before any token is entered. The _submit_ stays `authenticated`; schema open, trigger guarded.
|
|
91
86
|
- **A declared schema is enforced at the door.** `POST /workflows/:name/runs` validates the body and 400s naming the
|
|
92
87
|
accepted shape — parity with what Gates already do for deliveries. No schema declared → accept anything (today's
|
|
93
|
-
behavior), and the Console falls back from a generated form to a raw-JSON textarea.
|
|
88
|
+
behavior), and the Console falls back from a generated form to a raw-JSON textarea. A wrapper's door is parsed under a
|
|
89
|
+
parent too, so a parent that passes less than a `workspace()` or a `pool()` requires fails at invoke, before any pod.
|
|
90
|
+
That is a defect of the parent's Machine, so it goes to the parent's `onError`, and a Pool's `itemInput` is held to
|
|
91
|
+
the door of a `workspace()` worker in the same way. A plain child Machine's door is a type: no wrapper reads it at run
|
|
92
|
+
time.
|
|
94
93
|
- **The door takes an idempotency key, out of band.** A start may carry one in a header, never in the body (a body field
|
|
95
94
|
would be one every door had to reserve); a repeat with the same key answers with the run the first one made. A
|
|
96
95
|
`dispatch` Effect sends its ledger row's id ([ADR-0071](0071-a-run-starts-a-run-through-a-dispatch-effect.md)), so a
|
|
97
|
-
start whose answer was lost is sent once where it may have arrived (ADR-0067) and makes one run.
|
|
98
|
-
|
|
96
|
+
start whose answer was lost is sent once where it may have arrived (ADR-0067) and makes one run. With a second header,
|
|
97
|
+
a start is a key-only ask: the door answers the run under the key, or that no run has it, and starts nothing.
|
|
98
|
+
`jr2 run` and the Console send none.
|
|
99
99
|
|
|
100
100
|
## Considered options
|
|
101
101
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# The Console renders with Preact, and the server erases the types
|
|
2
2
|
|
|
3
|
-
The Console ([ADR-0032](0032-the-console-unlocks-with-
|
|
4
|
-
reached ~1,500 lines, much of it hand-rolled DOM reconciliation (`memo()`, `paint()`, manual list diffing) —
|
|
5
|
-
library exists to own. Three standing constraints shaped the replacement: the repo is zero-build (Node 24
|
|
6
|
-
nothing compiles), the Console serves only its own assets (ADR-0032), and `@jr2/orchestrator` is a library
|
|
7
|
-
its files from disk wherever the process starts. A fourth arrived with this decision: Console source is
|
|
8
|
-
everything else.
|
|
3
|
+
The Console ([ADR-0032](0032-the-console-unlocks-with-a-callers-credential.md)) outgrew its framework-free form:
|
|
4
|
+
`main.js` reached ~1,500 lines, much of it hand-rolled DOM reconciliation (`memo()`, `paint()`, manual list diffing) —
|
|
5
|
+
work a view library exists to own. Three standing constraints shaped the replacement: the repo is zero-build (Node 24
|
|
6
|
+
strips types; nothing compiles), the Console serves only its own assets (ADR-0032), and `@jr2/orchestrator` is a library
|
|
7
|
+
that serves its files from disk wherever the process starts. A fourth arrived with this decision: Console source is
|
|
8
|
+
TypeScript like everything else.
|
|
9
9
|
|
|
10
10
|
## Decision
|
|
11
11
|
|
|
@@ -81,10 +81,11 @@ every build) and promotes incidental image properties into contract items.
|
|
|
81
81
|
because jr2's file supplies one; such an image sets `GIT_CONFIG_SYSTEM=/etc/gitconfig` itself, and jr2 then adds
|
|
82
82
|
nothing. With the variable set, `git config --system <key> <value>` writes jr2's file on the read-only volume and
|
|
83
83
|
fails (exit 4); without it, an Agent that runs as root wrote `/etc/gitconfig`. A `--global` or repo-local write works
|
|
84
|
-
as before. A
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
`
|
|
84
|
+
as before. A raw `kubectl exec` shell does not get the variable, as it does not get the PATH append;
|
|
85
|
+
`jr2 workspace shell` starts through the spawner, as an Agent's `bash` does (ADR-0009). jr2's other git is out of
|
|
86
|
+
scope: the sync runs under `GIT_CONFIG_NOSYSTEM` (ADR-0067); the Sandbox Effector is another container with its own
|
|
87
|
+
committer rules (ADR-0068), and a credentialed `command` Effect runs there with `PATH`, `HOME` and `TMPDIR` alone, so
|
|
88
|
+
it gets no default identity.
|
|
88
89
|
|
|
89
90
|
- **The contract is the floor, and the preflight proves it.** What is inherent to "jr2's runtime in your filesystem" and
|
|
90
91
|
nothing more: a **glibc** base no older than the one jr2's node was built against (musl is out entirely), **`git`** on
|
|
@@ -101,14 +102,14 @@ every build) and promotes incidental image properties into contract items.
|
|
|
101
102
|
its first provision, in the `preflight` init container's log, with an error that names the fix (because "node did not
|
|
102
103
|
execute" is not actionable) — never as a mid-turn tool failure, and never as a converge refusal for a contract the
|
|
103
104
|
image was not under.
|
|
104
|
-
- **`workspace()` names the image as a STATIC OPTION, never
|
|
105
|
-
wrapper's business in the same way its
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
- **`workspace()` names the image as a STATIC OPTION, never from run input** — what the Sandbox is _made of_ is the
|
|
106
|
+
wrapper's business in the same way its clones are, and it is a fact about the MACHINE, not about a run: `jr2 up` finds
|
|
107
|
+
it by walking the registered Machines ([ADR-0049](0049-a-machine-carries-its-parts-and-composes-by-invoke.md)), and no
|
|
108
|
+
walk can evaluate a function of run input. The User Container rides the same rule and the same resolution as one more
|
|
109
|
+
option beside `image` (`user?: string`, [ADR-0005](0005-sandbox-pod-composition.md)). Neither is ever persisted: the
|
|
110
|
+
option rides the Machine (a parts map keyed on `machine.config`, as the vocabulary is) and the provisioning state
|
|
111
|
+
re-reads it at invoke time, so a restore provisions what the Machine carries now — and a resolved content-addressed
|
|
112
|
+
tag, which would outlive the image it names, never leaves the port's side of the seam at all.
|
|
112
113
|
- **A Menu-only Agent never gets a Sandbox Image.** `workspace: "none"` withholds the entire Working toolset (ADR-0028),
|
|
113
114
|
so there is no tooling to carry; the Instance Harness (ADR-0031) runs the stock image permanently. The feature has
|
|
114
115
|
exactly one seat, and it is the Workspace.
|