@sublang/playbook 11.0.0 → 12.1.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.
Files changed (49) hide show
  1. package/docs/cli.md +9 -6
  2. package/docs/configuration.md +15 -1
  3. package/docs/embedding.md +83 -1
  4. package/package.json +28 -3
  5. package/reference/sdlc/code.playbook/bin/repository-effects.js +501 -170
  6. package/reference/sdlc/code.playbook/bin/session-store.js +4 -1
  7. package/reference/sdlc/code.playbook/code.fsm.d.ts +22 -19
  8. package/reference/sdlc/code.playbook/code.fsm.js +116 -52
  9. package/reference/sdlc/code.playbook/code.fsm.ts +149 -64
  10. package/reference/sdlc/code.playbook/code.gears.md +40 -20
  11. package/reference/sdlc/code.playbook/code.playbook.js +23 -2
  12. package/reference/sdlc/code.playbook/code.playbook.ts +23 -2
  13. package/reference/sdlc/code.playbook/host-capabilities.d.ts +291 -0
  14. package/reference/sdlc/code.playbook/host-capabilities.js +40 -0
  15. package/reference/sdlc/code.playbook/playbook.config.template.yaml +18 -2
  16. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +13 -6
  17. package/reference/sdlc/decide.playbook/decide.fsm.js +54 -27
  18. package/reference/sdlc/decide.playbook/decide.fsm.ts +68 -29
  19. package/reference/sdlc/decide.playbook/decide.gears.md +25 -19
  20. package/reference/sdlc/decide.playbook/decide.playbook.js +11 -3
  21. package/reference/sdlc/decide.playbook/decide.playbook.ts +11 -3
  22. package/reference/sdlc/decide.playbook/decide.registry.js +1 -1
  23. package/reference/sdlc/decide.playbook/decide.registry.ts +1 -1
  24. package/reference/sdlc/dev.md +52 -0
  25. package/reference/sdlc/dev.playbook/dev.fsm.d.ts +261 -0
  26. package/reference/sdlc/dev.playbook/dev.fsm.js +723 -0
  27. package/reference/sdlc/dev.playbook/dev.fsm.ts +988 -0
  28. package/reference/sdlc/dev.playbook/dev.gears.md +91 -0
  29. package/reference/sdlc/dev.playbook/dev.playbook.d.ts +21 -0
  30. package/reference/sdlc/dev.playbook/dev.playbook.js +143 -0
  31. package/reference/sdlc/dev.playbook/dev.playbook.ts +246 -0
  32. package/reference/sdlc/dev.playbook/dev.registry.d.ts +40 -0
  33. package/reference/sdlc/dev.playbook/dev.registry.js +64 -0
  34. package/reference/sdlc/dev.playbook/dev.registry.ts +120 -0
  35. package/reference/sdlc/review.playbook/review.fsm.d.ts +15 -2
  36. package/reference/sdlc/review.playbook/review.fsm.js +77 -27
  37. package/reference/sdlc/review.playbook/review.fsm.ts +96 -30
  38. package/reference/sdlc/review.playbook/review.gears.md +52 -26
  39. package/reference/sdlc/review.playbook/review.playbook.js +17 -7
  40. package/reference/sdlc/review.playbook/review.playbook.ts +17 -7
  41. package/reference/sdlc/review.playbook/review.registry.js +1 -1
  42. package/reference/sdlc/review.playbook/review.registry.ts +1 -1
  43. package/slc/gears2fsm.md +20 -0
  44. package/slc/link.md +28 -5
  45. package/slc/text2gears.md +21 -1
  46. package/src/xstate-playbook-runtime.js +5 -2
  47. package/src/xstate-playbook-runtime.ts +5 -2
  48. package/src/xstate-runtime.js +13 -1
  49. package/src/xstate-runtime.ts +13 -1
@@ -4080,7 +4080,10 @@ function validateEffectLedgerAuthority(value, record, owner) {
4080
4080
  return authority;
4081
4081
  }
4082
4082
 
4083
- function applyEffectLedgerCommands(
4083
+ // Exported for the private worktree host-capability constructor in
4084
+ // repository-effects.js, so an external host's in-memory ledger applies the
4085
+ // exact command semantics the durable Captain record applies.
4086
+ export function applyEffectLedgerCommands(
4084
4087
  ledgerValue,
4085
4088
  authority,
4086
4089
  uncertain,
@@ -23,7 +23,6 @@ export type PlayerInput = {
23
23
  readonly callerInput: string;
24
24
  readonly runResults: string;
25
25
  readonly irNumber?: string;
26
- readonly irTask?: string;
27
26
  readonly pendingBossQuestion?: PendingBossQuestion;
28
27
  readonly bossReply?: string;
29
28
  };
@@ -36,16 +35,18 @@ export type PlayerOutput = {
36
35
  readonly coderOutput: string;
37
36
  readonly latestCommit: string;
38
37
  readonly irNumber: string;
39
- readonly irTask: string;
40
38
  } | {
41
39
  readonly guard: 'moreTasks';
42
40
  readonly coderOutput: string;
43
41
  readonly latestCommit: string;
42
+ readonly irNumber: string;
44
43
  readonly irTask: string;
45
44
  } | {
46
45
  readonly guard: 'finalTask';
47
46
  readonly coderOutput: string;
48
47
  readonly latestCommit: string;
48
+ readonly irNumber: string;
49
+ readonly irTask: string;
49
50
  } | {
50
51
  readonly guard: 'needsBossReply';
51
52
  readonly question: string;
@@ -57,8 +58,13 @@ export type PlaybookInput = {
57
58
  readonly text: string;
58
59
  };
59
60
  export type ReviewOutput = {
60
- readonly approvedCommit: 'latest';
61
61
  readonly noUnsettledFindings: true;
62
+ /**
63
+ * Exact repository revision at which the review scope was evaluated:
64
+ * the last review-fix commit when one landed, otherwise the clean
65
+ * round's receipt-observed HEAD (DR-045). Always present.
66
+ */
67
+ readonly evaluatedRevision: string;
62
68
  };
63
69
  export type CompactError = {
64
70
  readonly name: string;
@@ -66,16 +72,16 @@ export type CompactError = {
66
72
  };
67
73
  export type CodePlaybookOutput = {
68
74
  readonly status: 'complete';
69
- /** Exact identity of the latest CODE-owned commit. */
75
+ /** Exact identity of the last CODE-owned commit. */
70
76
  readonly lastCodeCommit: string;
71
- /** Exact Coder final text produced after that commit. */
72
- readonly lastCodeOutput: string;
77
+ /** Exact repository revision the final passing review evaluated. */
78
+ readonly finalEvaluatedRevision: string;
79
+ /** Every phase's review passed with no unsettled findings. */
80
+ readonly allReviewsPassed: true;
73
81
  } | {
74
82
  readonly status: 'review-failed';
75
- /** Exact identity of the latest CODE-owned commit. */
83
+ /** Exact identity of the last CODE-owned commit. */
76
84
  readonly lastCodeCommit: string;
77
- /** Exact Coder final text produced after that commit. */
78
- readonly lastCodeOutput: string;
79
85
  readonly error: CompactError;
80
86
  };
81
87
  export type CodingInput = {
@@ -88,7 +94,7 @@ export type CodingContext = {
88
94
  readonly latestCommit?: string;
89
95
  readonly irNumber?: string;
90
96
  readonly irTask?: string;
91
- readonly nextIrTask?: string;
97
+ readonly evaluatedRevision?: string;
92
98
  readonly phase?: CodePhase;
93
99
  readonly completion?: 'complete' | 'review-failed';
94
100
  readonly reviewError?: CompactError;
@@ -146,9 +152,6 @@ export declare const codingMachine: import("xstate").StateMachine<CodingContext,
146
152
  } | {
147
153
  type: "rememberFinalTask";
148
154
  params: import("xstate").NonReducibleUnknown;
149
- } | {
150
- type: "advanceToNextIrTask";
151
- params: import("xstate").NonReducibleUnknown;
152
155
  } | {
153
156
  type: "rememberPendingQuestion";
154
157
  params: import("xstate").NonReducibleUnknown;
@@ -211,16 +214,16 @@ export declare const codingMachine: import("xstate").StateMachine<CodingContext,
211
214
  params: unknown;
212
215
  }, never, "done" | "failed" | "awaitBossReply" | "ready" | "runFirstPhase" | "reviewFirstCommit" | "runIrTask" | "reviewIrTask" | "reportedReviewFailure", string, CodingInput, {
213
216
  readonly status: "complete";
214
- /** Exact identity of the latest CODE-owned commit. */
217
+ /** Exact identity of the last CODE-owned commit. */
215
218
  readonly lastCodeCommit: string;
216
- /** Exact Coder final text produced after that commit. */
217
- readonly lastCodeOutput: string;
219
+ /** Exact repository revision the final passing review evaluated. */
220
+ readonly finalEvaluatedRevision: string;
221
+ /** Every phase's review passed with no unsettled findings. */
222
+ readonly allReviewsPassed: true;
218
223
  } | {
219
224
  readonly status: "review-failed";
220
- /** Exact identity of the latest CODE-owned commit. */
225
+ /** Exact identity of the last CODE-owned commit. */
221
226
  readonly lastCodeCommit: string;
222
- /** Exact Coder final text produced after that commit. */
223
- readonly lastCodeOutput: string;
224
227
  readonly error: CompactError;
225
228
  }, import("xstate").EventObject, import("xstate").MetaObject, {
226
229
  id: "code";
@@ -3,56 +3,70 @@
3
3
  //
4
4
  // Generated by slc/gears2fsm.md from ./code.gears.md.
5
5
  import { assign, fromPromise, setup } from 'xstate';
6
+ const APPENDED_PHASE_PROMPT = [
7
+ 'Keep to the original intent and follow what it asks.',
8
+ 'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
9
+ "Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
10
+ 'Make the commit message explain concisely what changed and why, including relevant verification.',
11
+ 'Identify every new commit you make.',
12
+ 'Coder is <coder-llm>.',
13
+ ];
6
14
  const FIRST_PHASE_PROMPT = [
7
15
  '> <caller-input>',
8
16
  '> <run-results>',
9
17
  '',
10
- 'Assess whether the coding intent can be completed well in one commit.',
11
- 'If yes, implement and test it, update the affected specs, and ensure @specs/map.md remains accurate.',
12
- 'Otherwise, decompose it into tasks sized to exactly one commit each, add a new IR under @specs/intents, and do not implement any IR task in this phase.',
18
+ 'First determine whether the coding request starts a new coding intent or continues an existing IR with unfinished work.',
19
+ 'If the request may continue an existing IR but does not identify it unambiguously, ask Boss before changing files.',
20
+ '',
21
+ 'For a new coding intent, assess whether it can be completed well in one commit.',
22
+ 'If it can, implement and test it, update the affected specs, and ensure @specs/map.md remains accurate.',
23
+ 'If it cannot, decompose it into tasks sized to exactly one commit each, add a new IR under @specs/intents, and do not implement any IR task in this phase.',
13
24
  'Plan affected spec updates before, with, or after their corresponding code changes, either as standalone IR tasks or as explicit work within related tasks.',
14
25
  '',
26
+ 'For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
27
+ 'Do not implement a later task in this phase.',
28
+ "Mark the IR's progress and deliverables when relevant.",
29
+ 'If the IR will be finished after this phase, double-check that all acceptance criteria are met.',
30
+ '',
15
31
  'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
16
32
  '',
17
- 'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
18
- "Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
19
- 'Make the commit message explain concisely what changed and why, including relevant verification.',
20
- 'Coder is <coder-llm>; format the model token in conventional human form.',
33
+ ...APPENDED_PHASE_PROMPT,
21
34
  ].join('\n');
22
35
  const IR_TASK_PROMPT = [
23
- '> <ir-task>',
36
+ '> <caller-input>',
37
+ '> <ir-number>',
24
38
  '> <run-results>',
25
39
  '',
26
- 'Read IR-<#> and implement exactly the next unfinished task, including corresponding tests or specs if any.',
40
+ 'Read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
27
41
  'Do not implement a later task in this phase.',
28
42
  "Mark the IR's progress and deliverables when relevant.",
29
43
  'If the IR will be finished after this phase, double-check that all acceptance criteria are met.',
30
44
  '',
31
- 'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
32
- "Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
33
- 'Make the commit message explain concisely what changed and why, including relevant verification.',
34
- 'Coder is <coder-llm>; format the model token in conventional human form.',
45
+ ...APPENDED_PHASE_PROMPT,
35
46
  ].join('\n');
47
+ const NEEDS_BOSS_REPLY_RESULT = "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.";
36
48
  const FIRST_PHASE_RESULTS = {
37
49
  directCommit: 'Coder completed and committed the direct implementation phase. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.',
38
- irCommit: 'Coder created and committed a new IR without implementing an IR task. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber`, and `irTask` naming the exact next unfinished task.',
39
- needsBossReply: "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.",
50
+ irCommit: 'Coder created and committed a new IR without implementing an IR task. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irNumber` identifying the created IR.',
51
+ moreTasks: 'Coder continued an existing IR, completed and committed exactly its next unfinished task, and at least one task remains. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.',
52
+ finalTask: 'Coder continued an existing IR and completed and committed its final task. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.',
53
+ needsBossReply: NEEDS_BOSS_REPLY_RESULT,
40
54
  };
41
55
  const IR_TASK_RESULTS = {
42
- moreTasks: 'Coder completed and committed the current IR task and at least one task remains. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irTask` naming the exact next unfinished task.',
43
- finalTask: 'Coder completed and committed the final IR task. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.',
44
- needsBossReply: "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.",
56
+ moreTasks: "Coder completed and committed exactly the IR's next unfinished task and at least one task remains. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.",
57
+ finalTask: "Coder completed and committed the IR's final task. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.",
58
+ needsBossReply: NEEDS_BOSS_REPLY_RESULT,
45
59
  };
46
60
  const STATE_DESCRIPTIONS = {
47
61
  ready: 'Waiting for a coding intent.',
48
- runFirstPhase: 'Coder is implementing one direct phase or committing a new intent record.',
49
- reviewFirstCommit: 'The REVIEW playbook is checking the first phase commit.',
62
+ runFirstPhase: 'Coder is running the first coding phase: a direct implementation, a new intent record, or an existing intent-record task.',
63
+ reviewFirstCommit: 'The REVIEW playbook is checking the direct or new-IR phase commit.',
50
64
  runIrTask: 'Coder is implementing exactly one unfinished intent-record task.',
51
65
  reviewIrTask: 'The REVIEW playbook is checking the latest task commit.',
52
66
  awaitBossReply: 'Waiting for Boss to answer Coder.',
53
67
  failed: 'The coding workflow failed and is waiting for a new coding intent.',
54
68
  reportedReviewFailure: 'The coding workflow reported a REVIEW failure and the last code-owned commit.',
55
- done: 'The coding workflow completed after REVIEW found no unsettled findings.',
69
+ done: "The coding workflow completed after every phase's REVIEW passed with no unsettled findings.",
56
70
  };
57
71
  function playbookMeta(stateId, role) {
58
72
  return {
@@ -89,6 +103,11 @@ function hasCommittedOutput(event) {
89
103
  return (outputString(event, 'coderOutput') !== undefined &&
90
104
  outputString(event, 'latestCommit') !== undefined);
91
105
  }
106
+ function hasIrTaskOutput(event) {
107
+ return (hasCommittedOutput(event) &&
108
+ outputString(event, 'irNumber') !== undefined &&
109
+ outputString(event, 'irTask') !== undefined);
110
+ }
92
111
  function isDirectCommit({ event }) {
93
112
  return (outputGuard(event, 'directCommit') &&
94
113
  hasCommittedOutput(event));
@@ -96,17 +115,15 @@ function isDirectCommit({ event }) {
96
115
  function isIrCommit({ event }) {
97
116
  return (outputGuard(event, 'irCommit') &&
98
117
  hasCommittedOutput(event) &&
99
- outputString(event, 'irNumber') !== undefined &&
100
- outputString(event, 'irTask') !== undefined);
118
+ outputString(event, 'irNumber') !== undefined);
101
119
  }
102
120
  function isMoreTasks({ event }) {
103
121
  return (outputGuard(event, 'moreTasks') &&
104
- hasCommittedOutput(event) &&
105
- outputString(event, 'irTask') !== undefined);
122
+ hasIrTaskOutput(event));
106
123
  }
107
124
  function isFinalTask({ event }) {
108
125
  return (outputGuard(event, 'finalTask') &&
109
- hasCommittedOutput(event));
126
+ hasIrTaskOutput(event));
110
127
  }
111
128
  function needsBossReply({ event }) {
112
129
  return (outputGuard(event, 'needsBossReply') &&
@@ -116,11 +133,14 @@ function reviewOutput(event) {
116
133
  return isRecord(event) ? event.output : undefined;
117
134
  }
118
135
  function isReviewApprovedValue(value) {
119
- return (isRecord(value) &&
120
- Object.keys(value).sort().join('\0') ===
121
- ['approvedCommit', 'noUnsettledFindings'].sort().join('\0') &&
122
- value.approvedCommit === 'latest' &&
123
- value.noUnsettledFindings === true);
136
+ if (!isRecord(value))
137
+ return false;
138
+ if (Reflect.ownKeys(value).some((key) => typeof key !== 'string' ||
139
+ (key !== 'noUnsettledFindings' && key !== 'evaluatedRevision'))) {
140
+ return false;
141
+ }
142
+ return (value.noUnsettledFindings === true &&
143
+ isNonEmptyString(value.evaluatedRevision));
124
144
  }
125
145
  function reviewApprovedFor(phase) {
126
146
  return ({ context, event }) => context.phase === phase && isReviewApprovedValue(reviewOutput(event));
@@ -265,16 +285,23 @@ function quoteRelay(label, value) {
265
285
  const lines = value.split('\n');
266
286
  return [`> ${label}: ${lines[0] ?? ''}`, ...lines.slice(1).map((line) => `> ${line}`)].join('\n');
267
287
  }
268
- function initialReviewText(context) {
288
+ function reviewScopeLine(context) {
289
+ return (`> Review scope: the commit ${context.latestCommit ?? ''} ` +
290
+ 'from this coding phase and its resulting repository state.');
291
+ }
292
+ function newIntentReviewText(context) {
269
293
  return [
270
- quoteRelay('Initial intent', context.callerInput ?? ''),
294
+ quoteRelay('Original intent', context.callerInput ?? ''),
295
+ reviewScopeLine(context),
271
296
  quoteRelay('Coder output', context.coderOutput ?? ''),
272
297
  ].join('\n');
273
298
  }
274
299
  function irTaskReviewText(context) {
275
300
  return [
276
- quoteRelay('IR task', context.irTask ?? ''),
301
+ quoteRelay('Original intent', context.callerInput ?? ''),
302
+ reviewScopeLine(context),
277
303
  quoteRelay('Coder output', context.coderOutput ?? ''),
304
+ quoteRelay('Current IR task', context.irTask ?? ''),
278
305
  ].join('\n');
279
306
  }
280
307
  const machineSetup = setup({
@@ -320,7 +347,7 @@ const machineSetup = setup({
320
347
  latestCommit: undefined,
321
348
  irNumber: undefined,
322
349
  irTask: undefined,
323
- nextIrTask: undefined,
350
+ evaluatedRevision: undefined,
324
351
  phase: undefined,
325
352
  completion: undefined,
326
353
  reviewError: undefined,
@@ -340,8 +367,7 @@ const machineSetup = setup({
340
367
  coderOutput: outputString(event, 'coderOutput'),
341
368
  latestCommit: outputString(event, 'latestCommit'),
342
369
  irNumber: outputString(event, 'irNumber'),
343
- irTask: outputString(event, 'irTask'),
344
- nextIrTask: undefined,
370
+ irTask: undefined,
345
371
  phase: 'ir-created',
346
372
  pendingBossQuestion: undefined,
347
373
  bossReply: undefined,
@@ -349,7 +375,8 @@ const machineSetup = setup({
349
375
  rememberMoreTasks: assign(({ event }) => ({
350
376
  coderOutput: outputString(event, 'coderOutput'),
351
377
  latestCommit: outputString(event, 'latestCommit'),
352
- nextIrTask: outputString(event, 'irTask'),
378
+ irNumber: outputString(event, 'irNumber'),
379
+ irTask: outputString(event, 'irTask'),
353
380
  phase: 'ir-task-more',
354
381
  pendingBossQuestion: undefined,
355
382
  bossReply: undefined,
@@ -357,15 +384,12 @@ const machineSetup = setup({
357
384
  rememberFinalTask: assign(({ event }) => ({
358
385
  coderOutput: outputString(event, 'coderOutput'),
359
386
  latestCommit: outputString(event, 'latestCommit'),
360
- nextIrTask: undefined,
387
+ irNumber: outputString(event, 'irNumber'),
388
+ irTask: outputString(event, 'irTask'),
361
389
  phase: 'ir-task-final',
362
390
  pendingBossQuestion: undefined,
363
391
  bossReply: undefined,
364
392
  })),
365
- advanceToNextIrTask: assign(({ context }) => ({
366
- irTask: context.nextIrTask,
367
- nextIrTask: undefined,
368
- })),
369
393
  rememberPendingQuestion: assign(({ context, event }) => {
370
394
  const question = outputString(event, 'question');
371
395
  const output = playerOutput(event);
@@ -390,9 +414,14 @@ const machineSetup = setup({
390
414
  pendingBossQuestion: undefined,
391
415
  bossReply: undefined,
392
416
  }),
393
- completeSuccessfully: assign({
394
- completion: 'complete',
395
- reviewError: undefined,
417
+ completeSuccessfully: assign(({ event }) => {
418
+ const output = reviewOutput(event);
419
+ const approved = isReviewApprovedValue(output) ? output : undefined;
420
+ return {
421
+ completion: 'complete',
422
+ evaluatedRevision: approved?.evaluatedRevision,
423
+ reviewError: undefined,
424
+ };
396
425
  }),
397
426
  completeWithReviewFailure: assign(({ event }) => ({
398
427
  completion: 'review-failed',
@@ -426,19 +455,26 @@ export const codingMachine = machineSetup.createMachine({
426
455
  if (!isNonEmptyString(lastCodeCommit)) {
427
456
  throw new Error('CODE reached a final state without a commit identity');
428
457
  }
429
- const lastCodeOutput = context.coderOutput ?? '';
430
458
  if (context.completion === 'review-failed') {
431
459
  return {
432
460
  status: 'review-failed',
433
461
  lastCodeCommit,
434
- lastCodeOutput,
435
462
  error: context.reviewError ?? {
436
463
  name: 'Error',
437
464
  message: 'REVIEW failed without error detail.',
438
465
  },
439
466
  };
440
467
  }
441
- return { status: 'complete', lastCodeCommit, lastCodeOutput };
468
+ const finalEvaluatedRevision = context.evaluatedRevision;
469
+ if (!isNonEmptyString(finalEvaluatedRevision)) {
470
+ throw new Error('CODE completed without an evaluated repository revision');
471
+ }
472
+ return {
473
+ status: 'complete',
474
+ lastCodeCommit,
475
+ finalEvaluatedRevision,
476
+ allReviewsPassed: true,
477
+ };
442
478
  },
443
479
  states: {
444
480
  ready: {
@@ -503,6 +539,36 @@ export const codingMachine = machineSetup.createMachine({
503
539
  'rememberIrCommit',
504
540
  ],
505
541
  },
542
+ {
543
+ guard: 'isMoreTasks',
544
+ target: 'reviewIrTask',
545
+ actions: [
546
+ {
547
+ type: 'playbook.acceptedOutcome',
548
+ params: {
549
+ source: 'runFirstPhase',
550
+ target: 'reviewIrTask',
551
+ acceptedOutcome: 'moreTasks',
552
+ },
553
+ },
554
+ 'rememberMoreTasks',
555
+ ],
556
+ },
557
+ {
558
+ guard: 'isFinalTask',
559
+ target: 'reviewIrTask',
560
+ actions: [
561
+ {
562
+ type: 'playbook.acceptedOutcome',
563
+ params: {
564
+ source: 'runFirstPhase',
565
+ target: 'reviewIrTask',
566
+ acceptedOutcome: 'finalTask',
567
+ },
568
+ },
569
+ 'rememberFinalTask',
570
+ ],
571
+ },
506
572
  {
507
573
  guard: 'needsBossReply',
508
574
  target: 'awaitBossReply',
@@ -537,7 +603,7 @@ export const codingMachine = machineSetup.createMachine({
537
603
  stateId: 'reviewFirstCommit',
538
604
  sourceItem: 'CODE-2',
539
605
  playbookId: 'review',
540
- text: initialReviewText(context),
606
+ text: newIntentReviewText(context),
541
607
  }),
542
608
  onDone: [
543
609
  {
@@ -582,7 +648,6 @@ export const codingMachine = machineSetup.createMachine({
582
648
  ...(context.irNumber === undefined
583
649
  ? {}
584
650
  : { irNumber: context.irNumber }),
585
- ...(context.irTask === undefined ? {} : { irTask: context.irTask }),
586
651
  ...(context.pendingBossQuestion === undefined
587
652
  ? {}
588
653
  : { pendingBossQuestion: context.pendingBossQuestion }),
@@ -661,7 +726,6 @@ export const codingMachine = machineSetup.createMachine({
661
726
  {
662
727
  guard: 'reviewApprovedMoreTasks',
663
728
  target: 'runIrTask',
664
- actions: 'advanceToNextIrTask',
665
729
  },
666
730
  {
667
731
  guard: 'reviewApprovedFinalTask',