@jr2/orchestrator 0.5.1 → 0.6.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/docs/agents-block.md +2 -2
- package/docs/concepts/agents-and-turns.md +56 -20
- package/docs/concepts/architecture.md +30 -17
- package/docs/concepts/charts.md +6 -3
- package/docs/concepts/converge.md +3 -0
- package/docs/concepts/effects.md +86 -31
- package/docs/concepts/machines-and-workflows.md +9 -0
- package/docs/concepts/pools-and-sources.md +6 -6
- package/docs/concepts/secrets-and-trust.md +126 -42
- package/docs/concepts/the-menu.md +3 -0
- package/docs/concepts/workspaces.md +129 -51
- package/docs/get-started/an-agent-in-a-sandbox.md +21 -19
- 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 +17 -4
- package/docs/guides/bind-open-parts.md +5 -4
- package/docs/guides/cut-a-worktree.md +560 -0
- package/docs/guides/fix-a-failed-converge.md +1 -1
- package/docs/guides/let-an-agent-write-a-chart.md +5 -1
- package/docs/guides/look-inside-a-sandbox.md +18 -15
- package/docs/guides/move-from-kind-to-a-shared-cluster.md +16 -0
- package/docs/guides/push-and-sign-a-branch.md +67 -21
- package/docs/guides/run-a-command-or-call-a-url.md +84 -41
- package/docs/guides/run-one-worker-per-item.md +19 -18
- package/docs/guides/set-an-agents-access-and-dials.md +67 -22
- package/docs/guides/size-a-workspace.md +19 -6
- package/docs/guides/use-a-hosted-provider.md +6 -0
- package/docs/guides/watch-a-run.md +47 -24
- package/docs/guides/write-a-workflow.md +1 -1
- package/docs/llms.txt +33 -12
- package/docs/reference/authoring-surface/agent.md +34 -11
- package/docs/reference/authoring-surface/answer.md +19 -3
- package/docs/reference/authoring-surface/arg.md +27 -5
- package/docs/reference/authoring-surface/branchname.md +15 -0
- package/docs/reference/authoring-surface/command.md +36 -21
- package/docs/reference/authoring-surface/fail.md +17 -4
- package/docs/reference/authoring-surface/file.md +36 -16
- package/docs/reference/authoring-surface/http.md +108 -8
- package/docs/reference/authoring-surface/isready.md +14 -0
- package/docs/reference/authoring-surface/jr2setup.md +1 -1
- package/docs/reference/authoring-surface/open.md +36 -5
- package/docs/reference/authoring-surface/paramsof.md +22 -4
- package/docs/reference/authoring-surface/pool.md +4 -4
- package/docs/reference/authoring-surface/push.md +30 -12
- package/docs/reference/authoring-surface/readyof.md +21 -0
- package/docs/reference/authoring-surface/secret.md +25 -4
- package/docs/reference/authoring-surface/source.md +3 -3
- package/docs/reference/authoring-surface/type-agentdefinition.md +2 -2
- 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 +5 -6
- 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 +22 -4
- package/docs/reference/authoring-surface/type-customize.md +4 -2
- package/docs/reference/authoring-surface/type-dispatchoutput.md +8 -1
- package/docs/reference/authoring-surface/type-doorlessinput.md +19 -0
- 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-hostinjectedinput.md +2 -4
- 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 +7 -1
- package/docs/reference/authoring-surface/type-lateroutcome.md +21 -0
- 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-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 +2 -2
- package/docs/reference/authoring-surface/type-pushinput.md +22 -3
- package/docs/reference/authoring-surface/type-pushoutput.md +32 -7
- package/docs/reference/authoring-surface/type-reposlot.md +5 -5
- package/docs/reference/authoring-surface/type-sandboxoptions.md +11 -4
- package/docs/reference/authoring-surface/type-slothandles.md +5 -13
- package/docs/reference/authoring-surface/type-sourcespec.md +10 -10
- package/docs/reference/authoring-surface/type-workspaced.md +29 -8
- 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 +65 -37
- package/docs/reference/authoring-surface/worktree.md +71 -0
- package/docs/reference/authoring-surface.md +30 -10
- package/docs/reference/cli.md +1 -1
- package/docs/reference/config.md +13 -4
- package/docs/reference/decisions/0001-operator-owned-generic-sandbox.md +10 -9
- package/docs/reference/decisions/0002-duplex-streaming-actor.md +17 -10
- package/docs/reference/decisions/0004-worktree-provisioning-model.md +27 -21
- package/docs/reference/decisions/0005-sandbox-pod-composition.md +29 -19
- package/docs/reference/decisions/0006-agent-control-surface.md +6 -3
- package/docs/reference/decisions/0012-workspace-wrapper-machine.md +30 -30
- package/docs/reference/decisions/0013-the-agent-reaches-its-machine-through-a-container-it-cannot-read.md +35 -17
- package/docs/reference/decisions/0015-authoring-surface-absorbs-the-mechanism.md +8 -2
- package/docs/reference/decisions/0016-agent-turn-mechanics-are-internal.md +9 -7
- package/docs/reference/decisions/0017-pool-over-a-source-port.md +6 -4
- package/docs/reference/decisions/0019-one-converging-command-against-the-current-context.md +21 -9
- package/docs/reference/decisions/0021-workspace-continuity-is-a-lease-that-answers-back.md +2 -1
- package/docs/reference/decisions/0023-the-harness-prints-the-conversation.md +8 -9
- package/docs/reference/decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md +62 -65
- package/docs/reference/decisions/0027-the-harness-is-jr2s-own-server-flue-retires-the-wire-stays.md +9 -1
- package/docs/reference/decisions/0028-what-an-agent-may-do-to-the-workspace-is-part-of-its-definition.md +13 -12
- package/docs/reference/decisions/0031-menu-only-agents-run-on-the-instance-harness.md +4 -1
- package/docs/reference/decisions/0033-a-machine-declares-the-input-that-starts-it.md +18 -19
- package/docs/reference/decisions/0037-an-instance-builds-its-sandbox-images-jr2-injects-the-harness.md +8 -8
- package/docs/reference/decisions/0049-a-machine-carries-its-parts-and-composes-by-invoke.md +4 -4
- 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/0057-a-turn-is-its-frame-its-dials-and-whether-it-continues.md +24 -24
- package/docs/reference/decisions/0058-a-harness-answers-the-orchestrator-alone.md +5 -3
- package/docs/reference/decisions/0059-a-harness-holds-stand-ins-and-the-custodian-holds-the-keys.md +110 -67
- package/docs/reference/decisions/0062-jr2-briefs-the-agent-on-its-seat-never-on-its-task.md +101 -74
- package/docs/reference/decisions/0063-the-orchestrator-watches-the-cluster.md +5 -6
- package/docs/reference/decisions/0066-a-model-tier-runs-a-real-model-and-a-change-to-what-it-reads-runs-the-tier.md +2 -0
- package/docs/reference/decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md +181 -153
- package/docs/reference/decisions/0068-an-effector-holds-an-effects-secrets-and-signs-its-wire.md +74 -63
- package/docs/reference/decisions/0070-a-machine-lends-its-parts-to-a-chart.md +31 -20
- 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 +295 -0
- package/docs/reference/decisions/0076-an-agents-control-credential-lives-only-where-no-workspace-code-runs.md +320 -0
- package/docs/reference/decisions/0077-jr2-requires-landlock-on-every-sandbox-node.md +79 -0
- package/docs/reference/decisions.md +3 -0
- package/docs/reference/glossary.md +51 -12
- package/docs/reference/http-api.md +45 -7
- package/docs/reference/machines.md +0 -1
- package/docs/why.md +5 -4
- package/package.json +3 -3
- package/src/actor.ts +161 -35
- package/src/agent.ts +6 -6
- package/src/ambient.ts +3 -5
- package/src/chart.ts +186 -19
- package/src/charts.ts +9 -2
- package/src/config.ts +16 -6
- package/src/custodian.lua +17 -5
- package/src/custodian.ts +251 -77
- package/src/customize.ts +11 -3
- package/src/effect-checks.ts +50 -3
- package/src/effect.ts +343 -127
- package/src/effector-wire.ts +6 -3
- package/src/fail.ts +17 -4
- package/src/harness-client.ts +74 -31
- package/src/harness-env.ts +33 -0
- package/src/held-secrets.ts +125 -16
- package/src/holds.ts +3 -0
- package/src/http-effect.ts +88 -32
- package/src/http.ts +56 -17
- package/src/index.ts +15 -1
- package/src/internal.ts +1 -0
- package/src/kube-client.ts +6 -3
- package/src/machine-doc.ts +3 -4
- package/src/model-keys.ts +24 -0
- package/src/names.ts +19 -6
- package/src/notices.ts +443 -54
- package/src/open-body.ts +85 -19
- package/src/open.ts +36 -5
- package/src/parts.ts +40 -28
- package/src/party.ts +33 -0
- package/src/pick.ts +41 -7
- package/src/pool.ts +44 -23
- package/src/registration.ts +53 -11
- package/src/repo-identity.ts +7 -3
- package/src/repos.ts +20 -2
- package/src/run-host.ts +106 -20
- package/src/sandbox-effect.ts +217 -66
- package/src/sandbox-kube.ts +63 -16
- package/src/secret-placement.ts +9 -2
- package/src/setup.ts +86 -6
- package/src/tokens.ts +107 -32
- package/src/vocabulary.ts +23 -4
- package/src/wire.ts +10 -10
- package/src/workspace.ts +264 -253
- 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/concepts/effects.md
CHANGED
|
@@ -7,9 +7,10 @@ description: How a Machine runs a command, calls a URL, pushes a branch or start
|
|
|
7
7
|
|
|
8
8
|
An [Effect](../reference/glossary.md#effect) is work that a [Machine](../reference/glossary.md#machine) does outside
|
|
9
9
|
itself. jr2 has four kinds: `command` runs a program in the [Workspace](../reference/glossary.md#workspace), `http`
|
|
10
|
-
calls a URL, `push` pushes the
|
|
11
|
-
[Workflow](../reference/glossary.md#workflow).
|
|
12
|
-
[
|
|
10
|
+
calls a URL, `push` pushes the branch of a [Worktree](../reference/glossary.md#worktree), and `dispatch` starts a run of
|
|
11
|
+
a [Workflow](../reference/glossary.md#workflow). The Kit also gives
|
|
12
|
+
[`worktree()`](../reference/authoring-surface/worktree.md), an Effect on the `command` machinery that cuts a Worktree.
|
|
13
|
+
You write an Effect as an actor slot, beside the [Agents](../reference/glossary.md#agent) of the Machine.
|
|
13
14
|
|
|
14
15
|
## Why Effects exist
|
|
15
16
|
|
|
@@ -26,8 +27,8 @@ two times. A plain [xstate](https://xstate.js.org) promise has no such record.
|
|
|
26
27
|
|
|
27
28
|
Put an Effect under `actors` in [`jr2Setup`](../reference/authoring-surface/jr2setup.md). Invoke it by its key from a
|
|
28
29
|
state. The state's `onDone` gets the output of the Effect. This example is a Workflow with a Workspace. An Agent fixes
|
|
29
|
-
the code
|
|
30
|
-
the branch:
|
|
30
|
+
the code in a Worktree that the Machine cuts first. It can ask for a test run with the pick `run_tests`. When the Agent
|
|
31
|
+
picks `finished`, the Machine pushes the branch:
|
|
31
32
|
|
|
32
33
|
**Anthropic**
|
|
33
34
|
|
|
@@ -39,9 +40,11 @@ import {
|
|
|
39
40
|
command,
|
|
40
41
|
defineEvent,
|
|
41
42
|
fail,
|
|
43
|
+
isReady,
|
|
42
44
|
jr2Setup,
|
|
43
45
|
push,
|
|
44
46
|
workspace,
|
|
47
|
+
worktree,
|
|
45
48
|
type CommandOutput,
|
|
46
49
|
type Workspaced,
|
|
47
50
|
} from "@jr2/orchestrator";
|
|
@@ -61,6 +64,9 @@ const finished = defineEvent({
|
|
|
61
64
|
const runInput = z.object({ prompt: z.string(), branch: z.string() });
|
|
62
65
|
type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
|
|
63
66
|
|
|
67
|
+
// The Worktree that the coder, the tests and the push all name.
|
|
68
|
+
const appTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
|
|
69
|
+
|
|
64
70
|
const passed = (output: CommandOutput): boolean => output.outcome === "exited" && output.code === 0;
|
|
65
71
|
|
|
66
72
|
const body = jr2Setup({
|
|
@@ -71,7 +77,8 @@ const body = jr2Setup({
|
|
|
71
77
|
model: "anthropic/claude-haiku-5-5",
|
|
72
78
|
instructions: "You are a careful engineer. You commit your work on the branch.",
|
|
73
79
|
}),
|
|
74
|
-
//
|
|
80
|
+
// Three Effects. Each is an actor slot, beside the Agent.
|
|
81
|
+
cut: worktree(),
|
|
75
82
|
tests: command(["node", "--test"], { timeout: "5m" }),
|
|
76
83
|
publish: push(),
|
|
77
84
|
fail: fail(),
|
|
@@ -80,12 +87,20 @@ const body = jr2Setup({
|
|
|
80
87
|
id: "test-and-push",
|
|
81
88
|
context: ({ input }) => input,
|
|
82
89
|
on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
|
|
83
|
-
initial: "
|
|
90
|
+
initial: "cutting",
|
|
84
91
|
states: {
|
|
92
|
+
// The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
|
|
93
|
+
cutting: {
|
|
94
|
+
invoke: {
|
|
95
|
+
src: "cut",
|
|
96
|
+
input: ({ context }) => ({ path: appTree(context), branch: context.branch }),
|
|
97
|
+
onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
85
100
|
coding: {
|
|
86
101
|
invoke: {
|
|
87
102
|
src: "coder",
|
|
88
|
-
input: ({ context }) => ({ prompt: context.prompt, cwd: context
|
|
103
|
+
input: ({ context }) => ({ prompt: context.prompt, cwd: appTree(context) }),
|
|
89
104
|
},
|
|
90
105
|
on: { finished: "publishing" },
|
|
91
106
|
initial: "ready",
|
|
@@ -94,6 +109,7 @@ const body = jr2Setup({
|
|
|
94
109
|
testing: {
|
|
95
110
|
invoke: {
|
|
96
111
|
src: "tests",
|
|
112
|
+
input: ({ context }) => ({ worktree: appTree(context) }),
|
|
97
113
|
onDone: {
|
|
98
114
|
target: "ready",
|
|
99
115
|
// The Agent reads this text as the answer to its run_tests pick.
|
|
@@ -110,10 +126,12 @@ const body = jr2Setup({
|
|
|
110
126
|
publishing: {
|
|
111
127
|
invoke: {
|
|
112
128
|
src: "publish",
|
|
129
|
+
input: ({ context }) => ({ worktree: appTree(context) }),
|
|
113
130
|
onDone: [{ guard: ({ event }) => event.output.outcome === "pushed", target: "done" }, { target: "notPushed" }],
|
|
114
131
|
},
|
|
115
132
|
},
|
|
116
133
|
done: { type: "final" },
|
|
134
|
+
notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
|
|
117
135
|
// A Failure ends the body, and jr2 deletes the Sandbox with its commits. To keep them, wait at
|
|
118
136
|
// a Gate before this state.
|
|
119
137
|
notPushed: { invoke: { src: "fail", input: { reason: "the push did not publish the fix" } } },
|
|
@@ -125,7 +143,6 @@ const body = jr2Setup({
|
|
|
125
143
|
export const machine = workspace(body, {
|
|
126
144
|
input: runInput,
|
|
127
145
|
repos: { app: "https://github.com/you/app.git" },
|
|
128
|
-
spec: ({ input }) => ({ branch: input.branch }),
|
|
129
146
|
});
|
|
130
147
|
```
|
|
131
148
|
|
|
@@ -139,9 +156,11 @@ import {
|
|
|
139
156
|
command,
|
|
140
157
|
defineEvent,
|
|
141
158
|
fail,
|
|
159
|
+
isReady,
|
|
142
160
|
jr2Setup,
|
|
143
161
|
push,
|
|
144
162
|
workspace,
|
|
163
|
+
worktree,
|
|
145
164
|
type CommandOutput,
|
|
146
165
|
type Workspaced,
|
|
147
166
|
} from "@jr2/orchestrator";
|
|
@@ -161,6 +180,9 @@ const finished = defineEvent({
|
|
|
161
180
|
const runInput = z.object({ prompt: z.string(), branch: z.string() });
|
|
162
181
|
type BodyInput = Workspaced<z.infer<typeof runInput>, "app">;
|
|
163
182
|
|
|
183
|
+
// The Worktree that the coder, the tests and the push all name.
|
|
184
|
+
const appTree = (context: BodyInput) => `${context.workspace.repos.app}/fix` as const;
|
|
185
|
+
|
|
164
186
|
const passed = (output: CommandOutput): boolean => output.outcome === "exited" && output.code === 0;
|
|
165
187
|
|
|
166
188
|
const body = jr2Setup({
|
|
@@ -171,7 +193,8 @@ const body = jr2Setup({
|
|
|
171
193
|
model: "local/qwen3.6-35b-a3b",
|
|
172
194
|
instructions: "You are a careful engineer. You commit your work on the branch.",
|
|
173
195
|
}),
|
|
174
|
-
//
|
|
196
|
+
// Three Effects. Each is an actor slot, beside the Agent.
|
|
197
|
+
cut: worktree(),
|
|
175
198
|
tests: command(["node", "--test"], { timeout: "5m" }),
|
|
176
199
|
publish: push(),
|
|
177
200
|
fail: fail(),
|
|
@@ -180,12 +203,20 @@ const body = jr2Setup({
|
|
|
180
203
|
id: "test-and-push",
|
|
181
204
|
context: ({ input }) => input,
|
|
182
205
|
on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
|
|
183
|
-
initial: "
|
|
206
|
+
initial: "cutting",
|
|
184
207
|
states: {
|
|
208
|
+
// The Workspace only clones. The Machine cuts the Worktree that the Agent works in.
|
|
209
|
+
cutting: {
|
|
210
|
+
invoke: {
|
|
211
|
+
src: "cut",
|
|
212
|
+
input: ({ context }) => ({ path: appTree(context), branch: context.branch }),
|
|
213
|
+
onDone: [{ guard: ({ event }) => isReady(event.output), target: "coding" }, { target: "notCut" }],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
185
216
|
coding: {
|
|
186
217
|
invoke: {
|
|
187
218
|
src: "coder",
|
|
188
|
-
input: ({ context }) => ({ prompt: context.prompt, cwd: context
|
|
219
|
+
input: ({ context }) => ({ prompt: context.prompt, cwd: appTree(context) }),
|
|
189
220
|
},
|
|
190
221
|
on: { finished: "publishing" },
|
|
191
222
|
initial: "ready",
|
|
@@ -194,6 +225,7 @@ const body = jr2Setup({
|
|
|
194
225
|
testing: {
|
|
195
226
|
invoke: {
|
|
196
227
|
src: "tests",
|
|
228
|
+
input: ({ context }) => ({ worktree: appTree(context) }),
|
|
197
229
|
onDone: {
|
|
198
230
|
target: "ready",
|
|
199
231
|
// The Agent reads this text as the answer to its run_tests pick.
|
|
@@ -210,10 +242,12 @@ const body = jr2Setup({
|
|
|
210
242
|
publishing: {
|
|
211
243
|
invoke: {
|
|
212
244
|
src: "publish",
|
|
245
|
+
input: ({ context }) => ({ worktree: appTree(context) }),
|
|
213
246
|
onDone: [{ guard: ({ event }) => event.output.outcome === "pushed", target: "done" }, { target: "notPushed" }],
|
|
214
247
|
},
|
|
215
248
|
},
|
|
216
249
|
done: { type: "final" },
|
|
250
|
+
notCut: { invoke: { src: "fail", input: { reason: "the Worktree was not cut" } } },
|
|
217
251
|
// A Failure ends the body, and jr2 deletes the Sandbox with its commits. To keep them, wait at
|
|
218
252
|
// a Gate before this state.
|
|
219
253
|
notPushed: { invoke: { src: "fail", input: { reason: "the push did not publish the fix" } } },
|
|
@@ -225,7 +259,6 @@ const body = jr2Setup({
|
|
|
225
259
|
export const machine = workspace(body, {
|
|
226
260
|
input: runInput,
|
|
227
261
|
repos: { app: "https://github.com/you/app.git" },
|
|
228
|
-
spec: ({ input }) => ({ branch: input.branch }),
|
|
229
262
|
});
|
|
230
263
|
```
|
|
231
264
|
|
|
@@ -242,6 +275,7 @@ example above, `jr2 up` prints these lines:
|
|
|
242
275
|
|
|
243
276
|
```text
|
|
244
277
|
effects: workflow "test-and-push" — what it runs outside itself (ADR-0067)
|
|
278
|
+
cut: worktree — 600s, in the Harness
|
|
245
279
|
tests: command ["node","--test"] — 300s, in the Harness
|
|
246
280
|
publish: push — 300s, in the Sandbox Effector
|
|
247
281
|
```
|
|
@@ -253,6 +287,7 @@ run `failed`:
|
|
|
253
287
|
|
|
254
288
|
```text
|
|
255
289
|
failures: workflow "test-and-push" — the states that fail the run (ADR-0073)
|
|
290
|
+
test-and-push.notCut: invokes fail
|
|
256
291
|
test-and-push.notPushed: invokes fail
|
|
257
292
|
test-and-push.failed: invokes fail
|
|
258
293
|
test-and-push.lost: invokes fail
|
|
@@ -268,10 +303,9 @@ argument that the input of the Effect fills. Thus an Agent's text cannot become
|
|
|
268
303
|
in the argv: `["sh", "-c", "…"]`. Then the result is the result of your script. The Agent can change the files that the
|
|
269
304
|
script runs, so do not trust its result more than the Agent.
|
|
270
305
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
option). The full output goes to the run's feed.
|
|
306
|
+
Under [Repo Slots](../reference/glossary.md#repo-slot), the input of a `command` names its Worktree: `{ worktree }`, a
|
|
307
|
+
Worktree that a cut made, or `/work/<slot>/default`. Its default timeout is 10 minutes. Its output keeps the end of what
|
|
308
|
+
the program wrote: 4096 characters by default (the `tail` option). The full output goes to the run's feed.
|
|
275
309
|
|
|
276
310
|
### `http`
|
|
277
311
|
|
|
@@ -413,15 +447,18 @@ export const machine = jr2Setup({
|
|
|
413
447
|
});
|
|
414
448
|
```
|
|
415
449
|
|
|
416
|
-
The body can also be a file of
|
|
417
|
-
The
|
|
450
|
+
The body can also be a file of a Worktree: `body: file(path)` (see [`file`](../reference/authoring-surface/file.md)).
|
|
451
|
+
The input then names the Worktree, `{ worktree }`, and a cut must have made it. A path alone does not name the target of
|
|
452
|
+
a credential. The default timeout of an `http` Effect is 30 seconds.
|
|
418
453
|
|
|
419
454
|
### `push`
|
|
420
455
|
|
|
421
|
-
[`push`](../reference/authoring-surface/push.md) pushes the
|
|
422
|
-
[Binding](../reference/glossary.md#binding) of its Repo Slot.
|
|
423
|
-
|
|
424
|
-
credential.
|
|
456
|
+
[`push`](../reference/authoring-surface/push.md) pushes the branch of a Worktree to the
|
|
457
|
+
[Binding](../reference/glossary.md#binding) of its Repo Slot. Its input names the Worktree, `{ worktree }`, and has no
|
|
458
|
+
branch. jr2 reads the branch from the cut of that path, so an Agent that moves `HEAD` does not change what jr2 pushes.
|
|
459
|
+
It uses the push credential of the `git.credentials` entry that the URL matches (see
|
|
460
|
+
[`git.credentials`](../reference/config.md#git-credentials)). The Agent never reads that credential. These options
|
|
461
|
+
change what a push publishes:
|
|
425
462
|
|
|
426
463
|
- `pin`: push the commit that the input names, such as a commit that a [Gate](../reference/glossary.md#gate) approved.
|
|
427
464
|
Without `pin`, jr2 pushes the tip of the Agent's branch when the Effect starts.
|
|
@@ -429,11 +466,18 @@ credential. These options change what a push publishes:
|
|
|
429
466
|
- `squash`: push one commit that holds all the changes, with the message that the input gives.
|
|
430
467
|
- `force`: replace what the remote branch holds.
|
|
431
468
|
|
|
432
|
-
With `pin`, the input gives `{ sha }`. With `squash`, it gives `{ message }`.
|
|
469
|
+
With `pin`, the input also gives `{ sha }`. With `squash`, it also gives `{ message }`.
|
|
433
470
|
|
|
434
471
|
After a signed or a squashed push, the commits on the remote are not the Agent's own commits. Then jr2 moves the Agent's
|
|
435
472
|
branch to the published commits. The Agent's files, index and stash do not change. The default timeout is 5 minutes.
|
|
436
473
|
|
|
474
|
+
### `worktree`
|
|
475
|
+
|
|
476
|
+
[`worktree()`](../reference/authoring-surface/worktree.md) cuts a Worktree, or reads the one that a cut made at that
|
|
477
|
+
path. Its outcomes are `ready`, `refused` and `unknown`. It runs in the Harness container, and it is idempotent. Each
|
|
478
|
+
thing that an Agent's word can cause is `refused`, and nothing changes. [Workspaces](workspaces.md) tells what a
|
|
479
|
+
Worktree is, and [Cut a Worktree](../guides/cut-a-worktree.md) tells how to use it.
|
|
480
|
+
|
|
437
481
|
### `dispatch`
|
|
438
482
|
|
|
439
483
|
[`dispatch`](../reference/authoring-surface/dispatch.md) starts a run of a Workflow. The new run is a run of its own: it
|
|
@@ -467,6 +511,7 @@ commands in it:
|
|
|
467
511
|
| The Effect | Where it runs |
|
|
468
512
|
| --------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
469
513
|
| A `command` that names no Effect secret | The [Harness](../reference/glossary.md#harness) container of the Sandbox |
|
|
514
|
+
| A `worktree()` | The Harness container of the Sandbox |
|
|
470
515
|
| A `command` that names an Effect secret | The [Sandbox Effector](../reference/glossary.md#sandbox-effector), against a commit |
|
|
471
516
|
| An `http` Effect | The [Instance Effector](../reference/glossary.md#instance-effector) |
|
|
472
517
|
| An `http` Effect with a Worktree file | The Sandbox Effector |
|
|
@@ -475,17 +520,18 @@ commands in it:
|
|
|
475
520
|
| A `dispatch` with a `target` | The Instance Effector |
|
|
476
521
|
|
|
477
522
|
A `command` that names no Effect secret runs beside the Agent's [Working tools](../reference/glossary.md#working-tools).
|
|
478
|
-
It runs as the Agent's user, with the tools of the [Sandbox Image](../reference/glossary.md#sandbox-image).
|
|
479
|
-
|
|
523
|
+
It runs as the Agent's user, with the tools of the [Sandbox Image](../reference/glossary.md#sandbox-image). The Harness
|
|
524
|
+
confines it with Landlock, as it confines the Agent's shell. It has the same
|
|
525
|
+
[Stand-ins](../reference/glossary.md#stand-in) as the Agent's shell, and no credential of its own.
|
|
480
526
|
|
|
481
527
|
A `command` that names an Effect secret in its `env` runs in the Sandbox Effector, a container in which the Agent runs
|
|
482
528
|
nothing. It does not run in the live Worktree. It runs against one commit: the tip of the branch when it starts, or with
|
|
483
529
|
`pin`, the commit that its input names. When the Worktree held changes that the command did not see, its output has
|
|
484
530
|
`dirty: true`.
|
|
485
531
|
|
|
486
|
-
A `command`, a `push` and an `http` Effect with a file body need a Workspace. Invoke them from the body
|
|
487
|
-
[`workspace()`](../reference/authoring-surface/workspace.md). [Architecture](architecture.md) tells what each
|
|
488
|
-
is, and [Secrets and trust](secrets-and-trust.md) tells how an Effector holds the Effect secrets.
|
|
532
|
+
A `command`, a `worktree()`, a `push` and an `http` Effect with a file body need a Workspace. Invoke them from the body
|
|
533
|
+
of a [`workspace()`](../reference/authoring-surface/workspace.md). [Architecture](architecture.md) tells what each
|
|
534
|
+
Effector is, and [Secrets and trust](secrets-and-trust.md) tells how an Effector holds the Effect secrets.
|
|
489
535
|
|
|
490
536
|
## Every outcome is output
|
|
491
537
|
|
|
@@ -493,6 +539,7 @@ An Effect gives each outcome to `onDone`, a failure too. Route on the `outcome`
|
|
|
493
539
|
|
|
494
540
|
| Kind | The outcomes | The output type |
|
|
495
541
|
| ---------- | --------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
542
|
+
| `worktree` | `ready`, `refused`, `unknown` | [`WorktreeOutput`](../reference/authoring-surface/type-worktreeoutput.md) |
|
|
496
543
|
| `command` | `exited`, `timeout`, `killed`, `unknown` | [`CommandOutput`](../reference/authoring-surface/type-commandoutput.md) |
|
|
497
544
|
| `http` | `response`, `unreachable`, `refused`, `unknown` | [`HttpOutput`](../reference/authoring-surface/type-httpoutput.md) |
|
|
498
545
|
| `push` | `pushed`, `rejected`, `diverged`, `unknown` | [`PushOutput`](../reference/authoring-surface/type-pushoutput.md) |
|
|
@@ -502,6 +549,11 @@ A test that fails is `exited` with a code that is not 0. A program that the cont
|
|
|
502
549
|
code 127. An HTTP 4xx or 5xx status is a `response`. A Workflow that refuses the run input of a `dispatch` is `refused`,
|
|
503
550
|
with the status code and the report of the refusal. `unknown` means that jr2 cannot tell what the Effect did.
|
|
504
551
|
|
|
552
|
+
Each union is open: it holds a stand-in member, `LaterOutcome`, so a later release can add an outcome and your Machine
|
|
553
|
+
still works. Route on what you know, and give the rest a last arm with no guard. With no match, the run stays in that
|
|
554
|
+
state and shows no fault. So `createMachine`, `jr2 check`, `jr2 up` and the Chart door refuse an `onDone` with a guard
|
|
555
|
+
on its last arm. An invoke with no `onDone` is legal: the Effect settles, and the state stays.
|
|
556
|
+
|
|
505
557
|
The `onError` route of an Effect is only for a defect in the Machine or in the config. For example, the Effect is not in
|
|
506
558
|
a Workspace, or an `arg()` has no value, or an Open part has no binding. An outcome that means that the work did not
|
|
507
559
|
succeed goes to a state that invokes [`fail`](../reference/authoring-surface/fail.md). Then the run ends `failed`, with
|
|
@@ -521,6 +573,7 @@ jr2 records its output. After an Orchestrator restart, jr2 reads the record:
|
|
|
521
573
|
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
522
574
|
| A `command` that names no Effect secret | jr2 attaches to it again. When the Harness does not know it, the outcome is `unknown`. It never runs again. |
|
|
523
575
|
| A `command` that names an Effect secret | jr2 follows it. When the Sandbox Effector lost it, jr2 runs it again against the same commit. With no commit recorded, the outcome is `unknown`. |
|
|
576
|
+
| A `worktree()` | jr2 attaches to it again. When the Harness does not know it, jr2 sends it again: a cut is idempotent. |
|
|
524
577
|
| A `push` | jr2 follows it. When the Sandbox Effector lost it, jr2 pushes the same commit again, and no other commit. With no commit recorded, the outcome is `unknown`. |
|
|
525
578
|
| An `http` Effect | jr2 follows it. When no Effector holds it, jr2 sends it again only if it is `idempotent`. Else the outcome is `unknown`. |
|
|
526
579
|
| A `dispatch` | jr2 sends the start again, with the same key. The Orchestrator answers with the run that the key started, and it starts no second run. |
|
|
@@ -569,8 +622,10 @@ pick waits for its answer.
|
|
|
569
622
|
## Rules
|
|
570
623
|
|
|
571
624
|
- An Effect is a key of `actors`. `createMachine` refuses an Effect that you write directly in an `invoke`.
|
|
572
|
-
-
|
|
573
|
-
|
|
625
|
+
- The last arm of an Effect's `onDone` has no guard. `createMachine` refuses one that has:
|
|
626
|
+
`machine "<id>": state "<state>" (Effect "<slot>") has an onDone with no unguarded last arm — … (ADR-0075)`.
|
|
627
|
+
- A `command`, a `worktree()`, a `push` and an `http` Effect with a file body run only inside a `workspace()`.
|
|
628
|
+
- Under a Workspace with more than one Repo Slot, a `push` names its `slot`.
|
|
574
629
|
- The host of an `http` URL is literal. An Effect secret never goes in the host, the query or the fragment of a URL.
|
|
575
630
|
- Each Effect secret that an Effect names is in the `secrets` list of `jr2.config.ts`, with a value of 8 characters or
|
|
576
631
|
more. `jr2 up` refuses a name that is not in the list, and a value that is shorter.
|
|
@@ -143,6 +143,13 @@ has a door of its own. It does not use the door of the Machine inside it, becaus
|
|
|
143
143
|
item. Thus the wrapper declares the door, with its `input` option. `workspace()` refuses a body that has a zod `input`,
|
|
144
144
|
also a body that `setup()` made.
|
|
145
145
|
|
|
146
|
+
A `workspace()` that a parent Machine invokes parses its door too, before it makes a pod. When the parent passes less
|
|
147
|
+
than the door requires, the invoke fails with `workspace input invalid, before any pod: <zod message>`, and the parent's
|
|
148
|
+
`onError` takes it. This is a defect of the parent, so no pod is spent on it. The body gets the parsed value, as it does
|
|
149
|
+
on a root run: defaults are applied and unknown keys are removed. A `pool()` with a door does the same, and its error
|
|
150
|
+
starts `pool input invalid, before any worker`. The door of a plain child Machine is a type, and nothing parses it at
|
|
151
|
+
run time.
|
|
152
|
+
|
|
146
153
|
## The Vocabulary
|
|
147
154
|
|
|
148
155
|
The Vocabulary of a Machine is the list of the workflow events that it takes. Each event comes from
|
|
@@ -253,6 +260,8 @@ moves a Machine.
|
|
|
253
260
|
- A payload schema is a flat `z.object`, and it has no `sender` field. jr2 writes `sender` on each event that a party
|
|
254
261
|
sends.
|
|
255
262
|
- When a Machine declares a door, the Orchestrator refuses a start whose input does not agree with it.
|
|
263
|
+
- A `workspace()` or a `pool()` under a parent parses its door before it makes a pod. An input that does not agree fails
|
|
264
|
+
the invoke.
|
|
256
265
|
- The body of a `workspace()` declares no door. `workspace()` refuses a body that declares one.
|
|
257
266
|
- The output of a run is the root `output` of its Machine. jr2 refuses an `output` on a final state directly below the
|
|
258
267
|
root when the root has no `output`.
|
|
@@ -31,7 +31,7 @@ example runs with no model. A worker is usually a Machine with an [Agent](../ref
|
|
|
31
31
|
```ts
|
|
32
32
|
import { assign, fromPromise } from "xstate";
|
|
33
33
|
import { z } from "zod";
|
|
34
|
-
import { defineEvent, jr2Setup, pool, source } from "@jr2/orchestrator";
|
|
34
|
+
import { defineEvent, jr2Setup, pool, source, type PoolNextInput } from "@jr2/orchestrator";
|
|
35
35
|
|
|
36
36
|
type Ticket = { id: string; title: string; needs: string[] };
|
|
37
37
|
|
|
@@ -50,7 +50,7 @@ const claimed = new Set<string>();
|
|
|
50
50
|
|
|
51
51
|
// The Source: give the next ready ticket, or say how many tickets are not ready.
|
|
52
52
|
const tickets = source<Ticket>({
|
|
53
|
-
next: fromPromise(async ({ input }: { input:
|
|
53
|
+
next: fromPromise(async ({ input }: { input: PoolNextInput }) => {
|
|
54
54
|
const done = (id: string) => claimed.has(id) && !input.active.includes(id);
|
|
55
55
|
const waiting = backlog.filter((t) => !claimed.has(t.id));
|
|
56
56
|
const ready = waiting.find((t) => t.needs.every(done));
|
|
@@ -180,8 +180,9 @@ A Pool is a Machine. It can be the root of a Workflow, or a different Machine ca
|
|
|
180
180
|
[`source(spec)`](../reference/authoring-surface/source.md) declares where a Pool gets its items. A Source has three
|
|
181
181
|
parts ([`SourceSpec`](../reference/authoring-surface/type-sourcespec.md)):
|
|
182
182
|
|
|
183
|
-
- `next`: an [xstate](https://xstate.js.org) promise actor that claims the next ready item. Its input is
|
|
184
|
-
the ids of the items that workers have now
|
|
183
|
+
- `next`: an [xstate](https://xstate.js.org) promise actor that claims the next ready item. Its input is
|
|
184
|
+
`{ active, settled, input }`: the ids of the items that workers have now, the ids of the finished and failed items,
|
|
185
|
+
and the input of the Pool. Thus a Source can fan out over a plan with no state of its own.
|
|
185
186
|
- `wake`: an optional event, made with [`defineEvent`](../reference/authoring-surface/defineevent.md), that tells the
|
|
186
187
|
Pool that new work is ready.
|
|
187
188
|
- `pollEvery`: an optional time in milliseconds. While the Pool waits, it calls `next` again at this interval.
|
|
@@ -211,8 +212,7 @@ the issue tracker.
|
|
|
211
212
|
|
|
212
213
|
- `itemId` gives each item an id of its own, and the same id each time. The Pool uses it as the id of the worker and as
|
|
213
214
|
the key in `items`.
|
|
214
|
-
- `next` must not give an item
|
|
215
|
-
active ids only. Your Source keeps the finished items.
|
|
215
|
+
- `next` must not give an item in `active` or in `settled`.
|
|
216
216
|
- `cap` is a number, or a function of the run input. Without `cap`, one worker runs at a time.
|
|
217
217
|
- `onDrained` takes only `"final"`: the run ends.
|
|
218
218
|
- The Pool calls `next` again on `wake` or after `pollEvery` only while it waits: workers run and no item is ready.
|