@sublang/playbook 8.0.0 → 10.0.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 (66) hide show
  1. package/README.md +3 -3
  2. package/docs/cli.md +66 -23
  3. package/docs/configuration.md +13 -8
  4. package/docs/embedding.md +45 -14
  5. package/package.json +7 -3
  6. package/reference/sdlc/captain.md +14 -10
  7. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +33 -13
  8. package/reference/sdlc/captain.playbook/captain.fsm.js +80 -9
  9. package/reference/sdlc/captain.playbook/captain.fsm.ts +137 -18
  10. package/reference/sdlc/captain.playbook/captain.gears.md +10 -6
  11. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +5 -1
  12. package/reference/sdlc/captain.playbook/captain.playbook.js +151 -10
  13. package/reference/sdlc/captain.playbook/captain.playbook.ts +200 -14
  14. package/reference/sdlc/code.md +0 -1
  15. package/reference/sdlc/code.playbook/bin/interactive-session.js +170 -17
  16. package/reference/sdlc/code.playbook/bin/launch-config.js +136 -4
  17. package/reference/sdlc/code.playbook/bin/playbook.js +81 -4
  18. package/reference/sdlc/code.playbook/bin/repository-effects.js +2930 -0
  19. package/reference/sdlc/code.playbook/bin/run.js +365 -63
  20. package/reference/sdlc/code.playbook/bin/session-store.js +2877 -209
  21. package/reference/sdlc/code.playbook/code.fsm.d.ts +11 -1
  22. package/reference/sdlc/code.playbook/code.fsm.js +85 -29
  23. package/reference/sdlc/code.playbook/code.fsm.ts +95 -33
  24. package/reference/sdlc/code.playbook/code.gears.md +0 -2
  25. package/reference/sdlc/code.playbook/code.playbook.d.ts +5 -2
  26. package/reference/sdlc/code.playbook/code.playbook.js +67 -4
  27. package/reference/sdlc/code.playbook/code.playbook.ts +87 -8
  28. package/reference/sdlc/code.playbook/code.registry.d.ts +10 -3
  29. package/reference/sdlc/code.playbook/code.registry.js +10 -3
  30. package/reference/sdlc/code.playbook/code.registry.ts +23 -5
  31. package/reference/sdlc/code.playbook/playbook-captain.d.ts +99 -7
  32. package/reference/sdlc/code.playbook/playbook-captain.js +1894 -82
  33. package/reference/sdlc/code.playbook/playbook-captain.ts +2809 -109
  34. package/reference/sdlc/decide.md +0 -1
  35. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +8 -1
  36. package/reference/sdlc/decide.playbook/decide.fsm.js +80 -29
  37. package/reference/sdlc/decide.playbook/decide.fsm.ts +89 -31
  38. package/reference/sdlc/decide.playbook/decide.gears.md +0 -1
  39. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +15 -5
  40. package/reference/sdlc/decide.playbook/decide.playbook.js +1994 -191
  41. package/reference/sdlc/decide.playbook/decide.playbook.ts +3209 -404
  42. package/reference/sdlc/decide.playbook/decide.registry.d.ts +7 -3
  43. package/reference/sdlc/decide.playbook/decide.registry.js +10 -3
  44. package/reference/sdlc/decide.playbook/decide.registry.ts +20 -5
  45. package/reference/sdlc/review.playbook/review.fsm.d.ts +7 -0
  46. package/reference/sdlc/review.playbook/review.fsm.js +133 -12
  47. package/reference/sdlc/review.playbook/review.fsm.ts +140 -12
  48. package/reference/sdlc/review.playbook/review.playbook.d.ts +5 -2
  49. package/reference/sdlc/review.playbook/review.playbook.js +78 -4
  50. package/reference/sdlc/review.playbook/review.playbook.ts +95 -8
  51. package/reference/sdlc/review.playbook/review.registry.d.ts +10 -3
  52. package/reference/sdlc/review.playbook/review.registry.js +10 -3
  53. package/reference/sdlc/review.playbook/review.registry.ts +23 -5
  54. package/slc/gears2fsm.md +25 -7
  55. package/slc/link.md +727 -82
  56. package/src/accepted-outcome.d.ts +18 -0
  57. package/src/accepted-outcome.js +94 -0
  58. package/src/accepted-outcome.ts +140 -0
  59. package/src/runtime.d.ts +165 -3
  60. package/src/runtime.ts +214 -2
  61. package/src/xstate-playbook-runtime.d.ts +162 -13
  62. package/src/xstate-playbook-runtime.js +3344 -564
  63. package/src/xstate-playbook-runtime.ts +4873 -637
  64. package/src/xstate-runtime.d.ts +76 -8
  65. package/src/xstate-runtime.js +1001 -64
  66. package/src/xstate-runtime.ts +1640 -91
@@ -122,6 +122,13 @@ export declare const codingMachine: import("xstate").StateMachine<CodingContext,
122
122
  logic: import("xstate").PromiseActorLogic<PlayerOutput, PlayerInput, import("xstate").EventObject>;
123
123
  id: string | undefined;
124
124
  }, {
125
+ type: "playbook.acceptedOutcome";
126
+ params: {
127
+ readonly source: string;
128
+ readonly target: string;
129
+ readonly acceptedOutcome: string;
130
+ };
131
+ } | {
125
132
  type: "rememberActorError";
126
133
  params: import("xstate").NonReducibleUnknown;
127
134
  } | {
@@ -202,7 +209,7 @@ export declare const codingMachine: import("xstate").StateMachine<CodingContext,
202
209
  } | {
203
210
  type: "resumesIrTask";
204
211
  params: unknown;
205
- }, never, "done" | "failed" | "awaitBossReply" | "ready" | "runFirstPhase" | "reviewFirstCommit" | "runIrTask" | "reviewIrTask", string, CodingInput, {
212
+ }, never, "done" | "failed" | "awaitBossReply" | "ready" | "runFirstPhase" | "reviewFirstCommit" | "runIrTask" | "reviewIrTask" | "reportedReviewFailure", string, CodingInput, {
206
213
  readonly status: "complete";
207
214
  /** Exact identity of the latest CODE-owned commit. */
208
215
  readonly lastCodeCommit: string;
@@ -239,6 +246,9 @@ export declare const codingMachine: import("xstate").StateMachine<CodingContext,
239
246
  readonly failed: {
240
247
  id: "failed";
241
248
  };
249
+ readonly reportedReviewFailure: {
250
+ id: "reportedReviewFailure";
251
+ };
242
252
  readonly done: {
243
253
  id: "done";
244
254
  };
@@ -17,7 +17,6 @@ const FIRST_PHASE_PROMPT = [
17
17
  'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
18
18
  "Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
19
19
  'Make the commit message explain concisely what changed and why, including relevant verification.',
20
- 'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
21
20
  'Coder is <coder-llm>; format the model token in conventional human form.',
22
21
  ].join('\n');
23
22
  const IR_TASK_PROMPT = [
@@ -32,7 +31,6 @@ const IR_TASK_PROMPT = [
32
31
  'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
33
32
  "Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
34
33
  'Make the commit message explain concisely what changed and why, including relevant verification.',
35
- 'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
36
34
  'Coder is <coder-llm>; format the model token in conventional human form.',
37
35
  ].join('\n');
38
36
  const FIRST_PHASE_RESULTS = {
@@ -53,6 +51,7 @@ const STATE_DESCRIPTIONS = {
53
51
  reviewIrTask: 'The REVIEW playbook is checking the latest task commit.',
54
52
  awaitBossReply: 'Waiting for Boss to answer Coder.',
55
53
  failed: 'The coding workflow failed and is waiting for a new coding intent.',
54
+ reportedReviewFailure: 'The coding workflow reported a REVIEW failure and the last code-owned commit.',
56
55
  done: 'The coding workflow completed after REVIEW found no unsettled findings.',
57
56
  };
58
57
  function playbookMeta(stateId, role) {
@@ -86,38 +85,28 @@ function outputString(event, field) {
86
85
  const value = playerOutput(event)?.[field];
87
86
  return isNonEmptyString(value) ? value : undefined;
88
87
  }
89
- function commitOutput(event) {
90
- const coderOutput = outputString(event, 'coderOutput');
91
- const latestCommit = outputString(event, 'latestCommit');
92
- const commitLines = coderOutput
93
- ?.split('\n')
94
- .filter((line) => line.startsWith('Commit: '));
95
- if (coderOutput === undefined ||
96
- latestCommit === undefined ||
97
- commitLines?.length !== 1 ||
98
- commitLines[0] !== `Commit: ${latestCommit}`) {
99
- return undefined;
100
- }
101
- return { coderOutput, latestCommit };
88
+ function hasCommittedOutput(event) {
89
+ return (outputString(event, 'coderOutput') !== undefined &&
90
+ outputString(event, 'latestCommit') !== undefined);
102
91
  }
103
92
  function isDirectCommit({ event }) {
104
93
  return (outputGuard(event, 'directCommit') &&
105
- commitOutput(event) !== undefined);
94
+ hasCommittedOutput(event));
106
95
  }
107
96
  function isIrCommit({ event }) {
108
97
  return (outputGuard(event, 'irCommit') &&
109
- commitOutput(event) !== undefined &&
98
+ hasCommittedOutput(event) &&
110
99
  outputString(event, 'irNumber') !== undefined &&
111
100
  outputString(event, 'irTask') !== undefined);
112
101
  }
113
102
  function isMoreTasks({ event }) {
114
103
  return (outputGuard(event, 'moreTasks') &&
115
- commitOutput(event) !== undefined &&
104
+ hasCommittedOutput(event) &&
116
105
  outputString(event, 'irTask') !== undefined);
117
106
  }
118
107
  function isFinalTask({ event }) {
119
108
  return (outputGuard(event, 'finalTask') &&
120
- commitOutput(event) !== undefined);
109
+ hasCommittedOutput(event));
121
110
  }
122
111
  function needsBossReply({ event }) {
123
112
  return (outputGuard(event, 'needsBossReply') &&
@@ -320,6 +309,7 @@ const machineSetup = setup({
320
309
  (event.questionId === undefined || event.questionId === 'runIrTask'),
321
310
  },
322
311
  actions: {
312
+ 'playbook.acceptedOutcome': (_args, _params) => undefined,
323
313
  startCoding: assign(({ context, event }) => {
324
314
  if (event.type !== 'START_CODE')
325
315
  return {};
@@ -486,17 +476,47 @@ export const codingMachine = machineSetup.createMachine({
486
476
  {
487
477
  guard: 'isDirectCommit',
488
478
  target: 'reviewFirstCommit',
489
- actions: 'rememberDirectCommit',
479
+ actions: [
480
+ {
481
+ type: 'playbook.acceptedOutcome',
482
+ params: {
483
+ source: 'runFirstPhase',
484
+ target: 'reviewFirstCommit',
485
+ acceptedOutcome: 'directCommit',
486
+ },
487
+ },
488
+ 'rememberDirectCommit',
489
+ ],
490
490
  },
491
491
  {
492
492
  guard: 'isIrCommit',
493
493
  target: 'reviewFirstCommit',
494
- actions: 'rememberIrCommit',
494
+ actions: [
495
+ {
496
+ type: 'playbook.acceptedOutcome',
497
+ params: {
498
+ source: 'runFirstPhase',
499
+ target: 'reviewFirstCommit',
500
+ acceptedOutcome: 'irCommit',
501
+ },
502
+ },
503
+ 'rememberIrCommit',
504
+ ],
495
505
  },
496
506
  {
497
507
  guard: 'needsBossReply',
498
508
  target: 'awaitBossReply',
499
- actions: 'rememberPendingQuestion',
509
+ actions: [
510
+ {
511
+ type: 'playbook.acceptedOutcome',
512
+ params: {
513
+ source: 'runFirstPhase',
514
+ target: 'awaitBossReply',
515
+ acceptedOutcome: 'needsBossReply',
516
+ },
517
+ },
518
+ 'rememberPendingQuestion',
519
+ ],
500
520
  },
501
521
  {
502
522
  target: 'failed',
@@ -530,14 +550,14 @@ export const codingMachine = machineSetup.createMachine({
530
550
  target: 'runIrTask',
531
551
  },
532
552
  {
533
- target: 'done',
553
+ target: 'reportedReviewFailure',
534
554
  actions: 'completeWithInvalidReviewOutput',
535
555
  },
536
556
  ],
537
557
  onError: [
538
558
  {
539
559
  guard: 'authoredReviewFailure',
540
- target: 'done',
560
+ target: 'reportedReviewFailure',
541
561
  actions: 'completeWithReviewFailure',
542
562
  },
543
563
  { target: 'failed', actions: 'rememberActorError' },
@@ -574,17 +594,47 @@ export const codingMachine = machineSetup.createMachine({
574
594
  {
575
595
  guard: 'isMoreTasks',
576
596
  target: 'reviewIrTask',
577
- actions: 'rememberMoreTasks',
597
+ actions: [
598
+ {
599
+ type: 'playbook.acceptedOutcome',
600
+ params: {
601
+ source: 'runIrTask',
602
+ target: 'reviewIrTask',
603
+ acceptedOutcome: 'moreTasks',
604
+ },
605
+ },
606
+ 'rememberMoreTasks',
607
+ ],
578
608
  },
579
609
  {
580
610
  guard: 'isFinalTask',
581
611
  target: 'reviewIrTask',
582
- actions: 'rememberFinalTask',
612
+ actions: [
613
+ {
614
+ type: 'playbook.acceptedOutcome',
615
+ params: {
616
+ source: 'runIrTask',
617
+ target: 'reviewIrTask',
618
+ acceptedOutcome: 'finalTask',
619
+ },
620
+ },
621
+ 'rememberFinalTask',
622
+ ],
583
623
  },
584
624
  {
585
625
  guard: 'needsBossReply',
586
626
  target: 'awaitBossReply',
587
- actions: 'rememberPendingQuestion',
627
+ actions: [
628
+ {
629
+ type: 'playbook.acceptedOutcome',
630
+ params: {
631
+ source: 'runIrTask',
632
+ target: 'awaitBossReply',
633
+ acceptedOutcome: 'needsBossReply',
634
+ },
635
+ },
636
+ 'rememberPendingQuestion',
637
+ ],
588
638
  },
589
639
  {
590
640
  target: 'failed',
@@ -619,14 +669,14 @@ export const codingMachine = machineSetup.createMachine({
619
669
  actions: 'completeSuccessfully',
620
670
  },
621
671
  {
622
- target: 'done',
672
+ target: 'reportedReviewFailure',
623
673
  actions: 'completeWithInvalidReviewOutput',
624
674
  },
625
675
  ],
626
676
  onError: [
627
677
  {
628
678
  guard: 'authoredReviewFailure',
629
- target: 'done',
679
+ target: 'reportedReviewFailure',
630
680
  actions: 'completeWithReviewFailure',
631
681
  },
632
682
  { target: 'failed', actions: 'rememberActorError' },
@@ -670,6 +720,12 @@ export const codingMachine = machineSetup.createMachine({
670
720
  START_CODE: { target: 'runFirstPhase', actions: 'startCoding' },
671
721
  },
672
722
  },
723
+ reportedReviewFailure: {
724
+ id: 'reportedReviewFailure',
725
+ description: STATE_DESCRIPTIONS.reportedReviewFailure,
726
+ meta: playbookMeta('reportedReviewFailure'),
727
+ type: 'final',
728
+ },
673
729
  done: {
674
730
  id: 'done',
675
731
  description: STATE_DESCRIPTIONS.done,
@@ -154,7 +154,6 @@ const FIRST_PHASE_PROMPT = [
154
154
  'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
155
155
  "Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
156
156
  'Make the commit message explain concisely what changed and why, including relevant verification.',
157
- 'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
158
157
  'Coder is <coder-llm>; format the model token in conventional human form.',
159
158
  ].join('\n');
160
159
 
@@ -170,7 +169,6 @@ const IR_TASK_PROMPT = [
170
169
  'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
171
170
  "Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
172
171
  'Make the commit message explain concisely what changed and why, including relevant verification.',
173
- 'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
174
172
  'Coder is <coder-llm>; format the model token in conventional human form.',
175
173
  ].join('\n');
176
174
 
@@ -202,6 +200,8 @@ const STATE_DESCRIPTIONS = {
202
200
  reviewIrTask: 'The REVIEW playbook is checking the latest task commit.',
203
201
  awaitBossReply: 'Waiting for Boss to answer Coder.',
204
202
  failed: 'The coding workflow failed and is waiting for a new coding intent.',
203
+ reportedReviewFailure:
204
+ 'The coding workflow reported a REVIEW failure and the last code-owned commit.',
205
205
  done: 'The coding workflow completed after REVIEW found no unsettled findings.',
206
206
  } as const;
207
207
 
@@ -244,36 +244,24 @@ function outputString(event: unknown, field: string): string | undefined {
244
244
  return isNonEmptyString(value) ? value : undefined;
245
245
  }
246
246
 
247
- function commitOutput(
248
- event: unknown,
249
- ): { readonly coderOutput: string; readonly latestCommit: string } | undefined {
250
- const coderOutput = outputString(event, 'coderOutput');
251
- const latestCommit = outputString(event, 'latestCommit');
252
- const commitLines = coderOutput
253
- ?.split('\n')
254
- .filter((line) => line.startsWith('Commit: '));
255
- if (
256
- coderOutput === undefined ||
257
- latestCommit === undefined ||
258
- commitLines?.length !== 1 ||
259
- commitLines[0] !== `Commit: ${latestCommit}`
260
- ) {
261
- return undefined;
262
- }
263
- return { coderOutput, latestCommit };
247
+ function hasCommittedOutput(event: unknown): boolean {
248
+ return (
249
+ outputString(event, 'coderOutput') !== undefined &&
250
+ outputString(event, 'latestCommit') !== undefined
251
+ );
264
252
  }
265
253
 
266
254
  function isDirectCommit({ event }: { event: unknown }): boolean {
267
255
  return (
268
256
  outputGuard(event, 'directCommit') &&
269
- commitOutput(event) !== undefined
257
+ hasCommittedOutput(event)
270
258
  );
271
259
  }
272
260
 
273
261
  function isIrCommit({ event }: { event: unknown }): boolean {
274
262
  return (
275
263
  outputGuard(event, 'irCommit') &&
276
- commitOutput(event) !== undefined &&
264
+ hasCommittedOutput(event) &&
277
265
  outputString(event, 'irNumber') !== undefined &&
278
266
  outputString(event, 'irTask') !== undefined
279
267
  );
@@ -282,7 +270,7 @@ function isIrCommit({ event }: { event: unknown }): boolean {
282
270
  function isMoreTasks({ event }: { event: unknown }): boolean {
283
271
  return (
284
272
  outputGuard(event, 'moreTasks') &&
285
- commitOutput(event) !== undefined &&
273
+ hasCommittedOutput(event) &&
286
274
  outputString(event, 'irTask') !== undefined
287
275
  );
288
276
  }
@@ -290,7 +278,7 @@ function isMoreTasks({ event }: { event: unknown }): boolean {
290
278
  function isFinalTask({ event }: { event: unknown }): boolean {
291
279
  return (
292
280
  outputGuard(event, 'finalTask') &&
293
- commitOutput(event) !== undefined
281
+ hasCommittedOutput(event)
294
282
  );
295
283
  }
296
284
 
@@ -551,6 +539,14 @@ const machineSetup = setup({
551
539
  (event.questionId === undefined || event.questionId === 'runIrTask'),
552
540
  },
553
541
  actions: {
542
+ 'playbook.acceptedOutcome': (
543
+ _args,
544
+ _params: {
545
+ readonly source: string;
546
+ readonly target: string;
547
+ readonly acceptedOutcome: string;
548
+ },
549
+ ) => undefined,
554
550
  startCoding: assign(({ context, event }) => {
555
551
  if (event.type !== 'START_CODE') return {};
556
552
  return {
@@ -724,17 +720,47 @@ export const codingMachine = machineSetup.createMachine({
724
720
  {
725
721
  guard: 'isDirectCommit',
726
722
  target: 'reviewFirstCommit',
727
- actions: 'rememberDirectCommit',
723
+ actions: [
724
+ {
725
+ type: 'playbook.acceptedOutcome',
726
+ params: {
727
+ source: 'runFirstPhase',
728
+ target: 'reviewFirstCommit',
729
+ acceptedOutcome: 'directCommit',
730
+ },
731
+ },
732
+ 'rememberDirectCommit',
733
+ ],
728
734
  },
729
735
  {
730
736
  guard: 'isIrCommit',
731
737
  target: 'reviewFirstCommit',
732
- actions: 'rememberIrCommit',
738
+ actions: [
739
+ {
740
+ type: 'playbook.acceptedOutcome',
741
+ params: {
742
+ source: 'runFirstPhase',
743
+ target: 'reviewFirstCommit',
744
+ acceptedOutcome: 'irCommit',
745
+ },
746
+ },
747
+ 'rememberIrCommit',
748
+ ],
733
749
  },
734
750
  {
735
751
  guard: 'needsBossReply',
736
752
  target: 'awaitBossReply',
737
- actions: 'rememberPendingQuestion',
753
+ actions: [
754
+ {
755
+ type: 'playbook.acceptedOutcome',
756
+ params: {
757
+ source: 'runFirstPhase',
758
+ target: 'awaitBossReply',
759
+ acceptedOutcome: 'needsBossReply',
760
+ },
761
+ },
762
+ 'rememberPendingQuestion',
763
+ ],
738
764
  },
739
765
  {
740
766
  target: 'failed',
@@ -768,14 +794,14 @@ export const codingMachine = machineSetup.createMachine({
768
794
  target: 'runIrTask',
769
795
  },
770
796
  {
771
- target: 'done',
797
+ target: 'reportedReviewFailure',
772
798
  actions: 'completeWithInvalidReviewOutput',
773
799
  },
774
800
  ],
775
801
  onError: [
776
802
  {
777
803
  guard: 'authoredReviewFailure',
778
- target: 'done',
804
+ target: 'reportedReviewFailure',
779
805
  actions: 'completeWithReviewFailure',
780
806
  },
781
807
  { target: 'failed', actions: 'rememberActorError' },
@@ -812,17 +838,47 @@ export const codingMachine = machineSetup.createMachine({
812
838
  {
813
839
  guard: 'isMoreTasks',
814
840
  target: 'reviewIrTask',
815
- actions: 'rememberMoreTasks',
841
+ actions: [
842
+ {
843
+ type: 'playbook.acceptedOutcome',
844
+ params: {
845
+ source: 'runIrTask',
846
+ target: 'reviewIrTask',
847
+ acceptedOutcome: 'moreTasks',
848
+ },
849
+ },
850
+ 'rememberMoreTasks',
851
+ ],
816
852
  },
817
853
  {
818
854
  guard: 'isFinalTask',
819
855
  target: 'reviewIrTask',
820
- actions: 'rememberFinalTask',
856
+ actions: [
857
+ {
858
+ type: 'playbook.acceptedOutcome',
859
+ params: {
860
+ source: 'runIrTask',
861
+ target: 'reviewIrTask',
862
+ acceptedOutcome: 'finalTask',
863
+ },
864
+ },
865
+ 'rememberFinalTask',
866
+ ],
821
867
  },
822
868
  {
823
869
  guard: 'needsBossReply',
824
870
  target: 'awaitBossReply',
825
- actions: 'rememberPendingQuestion',
871
+ actions: [
872
+ {
873
+ type: 'playbook.acceptedOutcome',
874
+ params: {
875
+ source: 'runIrTask',
876
+ target: 'awaitBossReply',
877
+ acceptedOutcome: 'needsBossReply',
878
+ },
879
+ },
880
+ 'rememberPendingQuestion',
881
+ ],
826
882
  },
827
883
  {
828
884
  target: 'failed',
@@ -857,14 +913,14 @@ export const codingMachine = machineSetup.createMachine({
857
913
  actions: 'completeSuccessfully',
858
914
  },
859
915
  {
860
- target: 'done',
916
+ target: 'reportedReviewFailure',
861
917
  actions: 'completeWithInvalidReviewOutput',
862
918
  },
863
919
  ],
864
920
  onError: [
865
921
  {
866
922
  guard: 'authoredReviewFailure',
867
- target: 'done',
923
+ target: 'reportedReviewFailure',
868
924
  actions: 'completeWithReviewFailure',
869
925
  },
870
926
  { target: 'failed', actions: 'rememberActorError' },
@@ -908,6 +964,12 @@ export const codingMachine = machineSetup.createMachine({
908
964
  START_CODE: { target: 'runFirstPhase', actions: 'startCoding' },
909
965
  },
910
966
  },
967
+ reportedReviewFailure: {
968
+ id: 'reportedReviewFailure',
969
+ description: STATE_DESCRIPTIONS.reportedReviewFailure,
970
+ meta: playbookMeta('reportedReviewFailure'),
971
+ type: 'final',
972
+ },
911
973
  done: {
912
974
  id: 'done',
913
975
  description: STATE_DESCRIPTIONS.done,
@@ -26,7 +26,6 @@ When the first coding phase begins, Captain shall prompt Coder:
26
26
  > Do not re-run tests or builds whose inputs have not changed since any previous reported run.
27
27
  > Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.
28
28
  > Make the commit message explain concisely what changed and why, including relevant verification.
29
- > Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.
30
29
  > Coder is <coder-llm>; format the model token in conventional human form.
31
30
 
32
31
  Results:
@@ -61,7 +60,6 @@ When a reviewed IR has a next unfinished task, Captain shall prompt Coder:
61
60
  > Do not re-run tests or builds whose inputs have not changed since any previous reported run.
62
61
  > Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.
63
62
  > Make the commit message explain concisely what changed and why, including relevant verification.
64
- > Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.
65
63
  > Coder is <coder-llm>; format the model token in conventional human form.
66
64
 
67
65
  Results:
@@ -1,8 +1,10 @@
1
- import { type XStatePromptIdentity } from '@sublang/playbook/xstate-runtime';
1
+ import { type XStatePlaybookRuntimeFactory, type XStatePlaybookRuntimeConstruction, type XStatePromptIdentity } from '@sublang/playbook/xstate-runtime';
2
2
  import { type CodingInput, type PlayerInput } from './code.fsm.js';
3
+ import type { PlaybookHostConstructionCapabilities } from './playbook-captain.js';
3
4
  import type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, PlayerResult, PlayerSessionStore } from '@sublang/playbook/runtime';
4
5
  export type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, PlayerResult, PlayerSessionStore, };
5
6
  export type CodePlaybookOptions = CodingInput;
7
+ export type CodePlaybookHostCapabilities = PlaybookHostConstructionCapabilities & XStatePlaybookRuntimeConstruction<CodePlaybookOptions, object>['hostCapabilities'];
6
8
  /**
7
9
  * Preserve authored Markdown quote markers around every line of relayed
8
10
  * runtime text. The generic composer preserves the marker itself; CODE's
@@ -12,6 +14,7 @@ declare function composePlayerPrompt(input: PlayerInput, promptIdentity: XStateP
12
14
  export declare const _internal: {
13
15
  composePlayerPrompt: typeof composePlayerPrompt;
14
16
  VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
17
+ UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string>;
15
18
  };
16
- declare const createPlaybookRuntime: PlaybookRuntimeFactory<CodePlaybookOptions>;
19
+ declare const createPlaybookRuntime: XStatePlaybookRuntimeFactory<XStatePlaybookRuntimeConstruction<CodePlaybookOptions, CodePlaybookHostCapabilities>, 3>;
17
20
  export default createPlaybookRuntime;
@@ -8,8 +8,8 @@
8
8
  // callerInput; pending Coder questions retain BOSS_REPLY
9
9
  // Adjudication: LLM judge per player state; coderOutput is carried verbatim
10
10
  // Nested call: literal review target through the shared bridge
11
- // Compat: artifact schema 2 / runtime ABI 1
12
- import { RUNTIME_ABI, createXStatePlaybookRuntime, snapshotJsonValue, } from '@sublang/playbook/xstate-runtime';
11
+ // Compat: artifact schema 3 / runtime ABI 1
12
+ import { createXStatePlaybookRuntime, snapshotJsonValue, } from '@sublang/playbook/xstate-runtime';
13
13
  import { codingMachine, } from './code.fsm.js';
14
14
  const OPTION_KEYS = new Set(['runResults']);
15
15
  const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
@@ -17,6 +17,9 @@ const CONTINUATION_PREAMBLE = 'You previously paused this task to ask Boss a que
17
17
  const VERBATIM_PAYLOAD_FIELDS = new Set([
18
18
  'coderOutput',
19
19
  ]);
20
+ const UNFINISHED_FINAL_STATE_IDS = new Set([
21
+ 'reportedReviewFailure',
22
+ ]);
20
23
  function snapshotCodeOptions(value) {
21
24
  const captured = snapshotJsonValue(value, 'CODE runtime options');
22
25
  if (captured === null ||
@@ -78,12 +81,24 @@ function composePlayerPrompt(input, promptIdentity) {
78
81
  export const _internal = {
79
82
  composePlayerPrompt,
80
83
  VERBATIM_PAYLOAD_FIELDS,
84
+ UNFINISHED_FINAL_STATE_IDS,
81
85
  };
82
86
  const runtimeSpec = {
83
87
  label: 'CODE',
84
- compat: { artifactSchema: 2, runtimeAbi: RUNTIME_ABI },
88
+ // DR-022 / slc/link.md: the declaration carries the value current at link
89
+ // time — a literal, never the loading engine's RUNTIME_ABI self-report,
90
+ // which would follow whatever engine loads the module and make the
91
+ // factory's skew check compare that engine with itself.
92
+ compat: { artifactSchema: 3, runtimeAbi: 1 },
85
93
  snapshotOptions: snapshotCodeOptions,
86
- entryEvent: { type: 'START_CODE', textField: 'callerInput' },
94
+ entryEvent: {
95
+ type: 'START_CODE',
96
+ textField: 'callerInput',
97
+ // `startCoding` copies the entry text here, so the failure-state retry
98
+ // reads it back from the persisted machine snapshot and survives a
99
+ // continued session (DR-034).
100
+ contextField: 'callerInput',
101
+ },
87
102
  roleStates: {
88
103
  runFirstPhase: {
89
104
  role: 'coder',
@@ -94,9 +109,57 @@ const runtimeSpec = {
94
109
  label: 'Coder is implementing exactly one unfinished intent-record task.',
95
110
  },
96
111
  },
112
+ outcomeAuthority: {
113
+ governedPlayerStates: {
114
+ runFirstPhase: {
115
+ directCommit: {
116
+ fields: {
117
+ coderOutput: 'presentation',
118
+ latestCommit: 'effect',
119
+ },
120
+ repositoryDisposition: 'one-descendant-commit',
121
+ },
122
+ irCommit: {
123
+ fields: {
124
+ coderOutput: 'presentation',
125
+ latestCommit: 'effect',
126
+ irNumber: 'semantic',
127
+ irTask: 'semantic',
128
+ },
129
+ repositoryDisposition: 'one-descendant-commit',
130
+ },
131
+ needsBossReply: {
132
+ fields: { question: 'presentation' },
133
+ repositoryDisposition: 'deferred',
134
+ },
135
+ },
136
+ runIrTask: {
137
+ moreTasks: {
138
+ fields: {
139
+ coderOutput: 'presentation',
140
+ latestCommit: 'effect',
141
+ irTask: 'semantic',
142
+ },
143
+ repositoryDisposition: 'one-descendant-commit',
144
+ },
145
+ finalTask: {
146
+ fields: {
147
+ coderOutput: 'presentation',
148
+ latestCommit: 'effect',
149
+ },
150
+ repositoryDisposition: 'one-descendant-commit',
151
+ },
152
+ needsBossReply: {
153
+ fields: { question: 'presentation' },
154
+ repositoryDisposition: 'deferred',
155
+ },
156
+ },
157
+ },
158
+ },
97
159
  composePlayerPrompt: (input, promptIdentity) => composePlayerPrompt(input, promptIdentity),
98
160
  verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
99
161
  controlContextFields: ['phase'],
162
+ unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
100
163
  transitionEventFields: ['callerInput', 'answer', 'questionId'],
101
164
  };
102
165
  const createPlaybookRuntime = createXStatePlaybookRuntime(codingMachine, runtimeSpec);