@jr2/machines 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/task.ts +36 -38
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jr2/machines",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "The Machines the jr2 kit ships: packaged xstate Machines that leave their parts Open for a composer to bind.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -35,14 +35,14 @@
35
35
  "peerDependencies": {
36
36
  "xstate": "^5.18.0",
37
37
  "zod": "^4.4.3",
38
- "@jr2/orchestrator": "0.1.4"
38
+ "@jr2/orchestrator": "0.2.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^26.0.1",
42
42
  "typescript": "^5.6.0",
43
43
  "xstate": "^5.18.0",
44
44
  "zod": "^4.4.3",
45
- "@jr2/orchestrator": "0.1.4"
45
+ "@jr2/orchestrator": "0.2.1"
46
46
  },
47
47
  "files": [
48
48
  "src"
package/src/task.ts CHANGED
@@ -8,8 +8,9 @@
8
8
  // cannot pay for one. A consumer binds both on the line that registers this Machine as a
9
9
  // Workflow, naming every slot. The ORDER is this Machine's convention, not the kit's (ADR-0051:
10
10
  // the handles keep declaration order and the kit reads nothing into it): the FIRST slot is the
11
- // one the coder works in, and any others are attached beside it for the coder to read (a library
12
- // the change targets, a handbook):
11
+ // one the coder works in — its Worktree is the `cwd` every Turn is framed with (ADR-0057), so the
12
+ // Working tools are rooted where the prompt says to work — and any others are attached beside it
13
+ // for the coder to read (a library the change targets, a handbook):
13
14
  //
14
15
  // export const machine = customize(task, {
15
16
  // repos: {
@@ -123,18 +124,12 @@ const requestChanges = defineEvent({
123
124
  type BodyInput = Workspaced<TaskInput & HostInjectedInput, string>;
124
125
 
125
126
  type BodyContext = BodyInput & {
126
- /**
127
- * Which conversation the coder's Turns ride. `request_changes` continues the SAME conversation —
128
- * one human steering one Agent wants the Agent to remember what it did (ADR-0054) — but a
129
- * terminal `agent.fault` means jr2 already rerolled that conversation and gave up on it
130
- * (ADR-0035), so there is nothing left to continue. Bumping the generation folds into the
131
- * conversation's disambiguator, and the next Turn starts a fresh conversation instead of
132
- * addressing a dead one.
133
- */
134
- generation: number;
135
- /** Turns COMPLETED on the current conversation. Zero means the next Turn is its first, so the
136
- * prompt has to carry the whole task and the worktree framing again — which is exactly what a
137
- * post-fault Turn needs and a continued one does not. */
127
+ /** Turns COMPLETED on the conversation the next Turn will land on. Zero means that Turn is its
128
+ * first, so the prompt has to carry the whole task and the worktree framing again — which is
129
+ * exactly what a post-fault Turn needs and a continued one does not. It counts for the PROMPT
130
+ * alone: the conversation itself is jr2's, and a terminal `agent.fault` burns it in the ledger
131
+ * so the next `continue` is virgin (ADR-0057). Re-briefing that Turn is what stays here, because
132
+ * jr2 cannot write the prompt. */
138
133
  turns: number;
139
134
  /** The coder's own account of the last finished Turn — the Gate's `summary`. */
140
135
  summary?: string;
@@ -144,11 +139,6 @@ type BodyContext = BodyInput & {
144
139
  notes?: string;
145
140
  };
146
141
 
147
- /** The conversation pin (ADR-0016): a workflow-chosen name, so every `working` Turn of a run
148
- * derives one deterministic instance id and the coder's context survives the round trip through
149
- * the Gate. The name is the slot key, because that is what the conversation is about. */
150
- const CONVERSATION = "coder";
151
-
152
142
  export const body = jr2Setup({
153
143
  types: {} as { context: BodyContext; input: BodyInput; output: TaskOutput },
154
144
  events: [finish, approve, requestChanges],
@@ -165,8 +155,8 @@ export const body = jr2Setup({
165
155
  workspace: "write",
166
156
  instructions: `You are a software engineer working alone on one task, in a container of your own.
167
157
 
168
- - Edit only inside the worktree the conversation names as yours. Nothing you write outside it
169
- survives. Any other checkout it names is there for you to read.
158
+ - Edit only inside the worktree your turn names as yours — your tools are already rooted in it.
159
+ Nothing you write outside it survives. Any other checkout the turn names is there for you to read.
170
160
  - Commit your work on the named branch. You cannot push, and you do not need to: a human reads the
171
161
  branch in this container before the run ends.
172
162
  - Read before you write, and prefer the smallest change that does the task.
@@ -176,7 +166,7 @@ export const body = jr2Setup({
176
166
  },
177
167
  }).createMachine({
178
168
  id: "task",
179
- context: ({ input }) => ({ ...input, generation: 0, turns: 0 }),
169
+ context: ({ input }) => ({ ...input, turns: 0 }),
180
170
  initial: "working",
181
171
 
182
172
  // Our Sandbox is gone — reaped, or replaced after an eviction (ADR-0021). The pod-local clone and
@@ -188,12 +178,17 @@ export const body = jr2Setup({
188
178
  invoke: {
189
179
  src: "coder",
190
180
  input: ({ context }) => ({
181
+ // This Turn's FRAME (ADR-0057): what it is about, and where it works. The `cwd` is
182
+ // stated rather than left to the single-slot default, because "the first slot is the one
183
+ // the coder edits" is this Machine's convention (`worktreeOf`) and a convention is
184
+ // stated — a consumer who binds a second slot must not change where the coder works.
191
185
  prompt: coderPrompt(context),
192
- // One conversation per generation (see `BodyContext.generation`). `scope` is the
193
- // disambiguator the pin already has for exactly this: it rides the derived iid, so a
194
- // post-fault Turn addresses a new conversation instead of a dead one.
195
- conversation: CONVERSATION,
196
- scope: `g${context.generation}`,
186
+ cwd: worktreeOf(context).path,
187
+ // One human steering one Agent wants the Agent to remember what it did (ADR-0054), so
188
+ // every `working` Turn lands on this Machine instance's one coder conversation. The
189
+ // boolean names nothing: the id is structural (ADR-0057), and a conversation jr2 has
190
+ // faulted is burned — the next `continue` mints a virgin one.
191
+ continue: true,
197
192
  // This run's Dials, if the caller set them (ADR-0018/0054) — passed straight through,
198
193
  // layered over the bound definition when the Submission starts.
199
194
  ...(context.model !== undefined ? { model: context.model } : {}),
@@ -212,14 +207,14 @@ export const body = jr2Setup({
212
207
  },
213
208
  // ADR-0016's ONE terminal telemetry, ROUTED: jr2 has already retried, nudged and rerolled
214
209
  // (ADR-0027/0035), so this is the end of that conversation, not of the run. Park at the
215
- // same Gate — the Workspace is still alive and the work so far is still on the branch —
216
- // and start the next Turn on a fresh conversation.
210
+ // same Gate — the Workspace is still alive and the work so far is still on the branch.
211
+ // The dead conversation is jr2's to bury (it bumps the epoch, so the next `continue` is
212
+ // virgin — ADR-0057); what belongs here is the RE-BRIEF, which `turns: 0` asks for.
217
213
  "agent.fault": {
218
214
  target: "review",
219
215
  actions: assign({
220
216
  reason: ({ event }) => event.reason,
221
217
  summary: undefined,
222
- generation: ({ context }) => context.generation + 1,
223
218
  turns: 0,
224
219
  }),
225
220
  },
@@ -312,14 +307,16 @@ function branchOf(input: TaskInput): string {
312
307
  }
313
308
 
314
309
  /**
315
- * The turn's framing. The FIRST Turn of a conversation carries the whole task and the geography,
316
- * because the conversation holds nothing yet; every Turn after it carries the human's notes alone,
317
- * because the coder remembers the rest (ADR-0054's continued conversation). A post-fault Turn is a
318
- * first Turn again — that is what `turns` counts.
310
+ * Half the Turn's Frame (ADR-0057) — what it is about. The FIRST Turn of a conversation carries
311
+ * the whole task and the geography, because the conversation holds nothing yet; every Turn after
312
+ * it carries the human's notes alone, because the coder remembers the rest (ADR-0054's continued
313
+ * conversation). A post-fault Turn is a first Turn again — that is what `turns` counts.
319
314
  *
320
- * The geography is every slot the handles carry (ADR-0051): the first is where the work goes, and
321
- * each other checkout is named by the consumer's own word for it, at the path the attach put it —
322
- * the only way the coder can learn what is beside it, since the package never knew.
315
+ * The geography is every slot the handles carry (ADR-0051): the first is where the work goes —
316
+ * and it is the Frame's `cwd`, so the Working tools are rooted there and the prose only says what
317
+ * the tools already do — and each other checkout is named by the consumer's own word for it, at
318
+ * the path the attach put it, the only way the coder can learn what is beside it since the package
319
+ * never knew.
323
320
  */
324
321
  function coderPrompt(context: BodyContext): string {
325
322
  const notes = context.notes ? `\n\nThe human reviewed your work and asks for changes:\n${context.notes}` : "";
@@ -332,7 +329,8 @@ function coderPrompt(context: BodyContext): string {
332
329
  .join("");
333
330
  return (
334
331
  `${context.prompt}${notes}\n\n` +
335
- `Work in ${worktree.path}, on branch ${branch}. Commit what you do there, then call finish.` +
332
+ `Work in ${worktree.path}, on branch ${branch} — your tools are rooted there, so a relative ` +
333
+ `path lands inside it. Commit what you do there, then call finish.` +
336
334
  (beside ? `\n\nAlso checked out beside it, for you to read:${beside}` : "")
337
335
  );
338
336
  }