@sublang/playbook 9.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 (64) hide show
  1. package/docs/cli.md +51 -8
  2. package/docs/embedding.md +38 -12
  3. package/package.json +7 -3
  4. package/reference/sdlc/captain.md +14 -10
  5. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +33 -13
  6. package/reference/sdlc/captain.playbook/captain.fsm.js +80 -9
  7. package/reference/sdlc/captain.playbook/captain.fsm.ts +137 -18
  8. package/reference/sdlc/captain.playbook/captain.gears.md +10 -6
  9. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +5 -1
  10. package/reference/sdlc/captain.playbook/captain.playbook.js +140 -10
  11. package/reference/sdlc/captain.playbook/captain.playbook.ts +188 -16
  12. package/reference/sdlc/code.md +0 -1
  13. package/reference/sdlc/code.playbook/bin/interactive-session.js +170 -17
  14. package/reference/sdlc/code.playbook/bin/launch-config.js +136 -4
  15. package/reference/sdlc/code.playbook/bin/playbook.js +81 -4
  16. package/reference/sdlc/code.playbook/bin/repository-effects.js +2930 -0
  17. package/reference/sdlc/code.playbook/bin/run.js +365 -63
  18. package/reference/sdlc/code.playbook/bin/session-store.js +2877 -209
  19. package/reference/sdlc/code.playbook/code.fsm.d.ts +7 -0
  20. package/reference/sdlc/code.playbook/code.fsm.js +74 -25
  21. package/reference/sdlc/code.playbook/code.fsm.ts +83 -29
  22. package/reference/sdlc/code.playbook/code.gears.md +0 -2
  23. package/reference/sdlc/code.playbook/code.playbook.d.ts +5 -2
  24. package/reference/sdlc/code.playbook/code.playbook.js +54 -2
  25. package/reference/sdlc/code.playbook/code.playbook.ts +75 -6
  26. package/reference/sdlc/code.playbook/code.registry.d.ts +10 -3
  27. package/reference/sdlc/code.playbook/code.registry.js +10 -3
  28. package/reference/sdlc/code.playbook/code.registry.ts +23 -5
  29. package/reference/sdlc/code.playbook/playbook-captain.d.ts +99 -7
  30. package/reference/sdlc/code.playbook/playbook-captain.js +1850 -72
  31. package/reference/sdlc/code.playbook/playbook-captain.ts +2759 -96
  32. package/reference/sdlc/decide.md +0 -1
  33. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +7 -0
  34. package/reference/sdlc/decide.playbook/decide.fsm.js +80 -29
  35. package/reference/sdlc/decide.playbook/decide.fsm.ts +89 -31
  36. package/reference/sdlc/decide.playbook/decide.gears.md +0 -1
  37. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +13 -5
  38. package/reference/sdlc/decide.playbook/decide.playbook.js +1712 -91
  39. package/reference/sdlc/decide.playbook/decide.playbook.ts +2677 -136
  40. package/reference/sdlc/decide.playbook/decide.registry.d.ts +7 -3
  41. package/reference/sdlc/decide.playbook/decide.registry.js +10 -3
  42. package/reference/sdlc/decide.playbook/decide.registry.ts +20 -5
  43. package/reference/sdlc/review.playbook/review.fsm.d.ts +7 -0
  44. package/reference/sdlc/review.playbook/review.fsm.js +133 -12
  45. package/reference/sdlc/review.playbook/review.fsm.ts +140 -12
  46. package/reference/sdlc/review.playbook/review.playbook.d.ts +5 -2
  47. package/reference/sdlc/review.playbook/review.playbook.js +65 -2
  48. package/reference/sdlc/review.playbook/review.playbook.ts +83 -6
  49. package/reference/sdlc/review.playbook/review.registry.d.ts +10 -3
  50. package/reference/sdlc/review.playbook/review.registry.js +10 -3
  51. package/reference/sdlc/review.playbook/review.registry.ts +23 -5
  52. package/slc/gears2fsm.md +6 -5
  53. package/slc/link.md +544 -41
  54. package/src/accepted-outcome.d.ts +18 -0
  55. package/src/accepted-outcome.js +94 -0
  56. package/src/accepted-outcome.ts +140 -0
  57. package/src/runtime.d.ts +164 -3
  58. package/src/runtime.ts +213 -2
  59. package/src/xstate-playbook-runtime.d.ts +149 -10
  60. package/src/xstate-playbook-runtime.js +2569 -270
  61. package/src/xstate-playbook-runtime.ts +4133 -490
  62. package/src/xstate-runtime.d.ts +59 -1
  63. package/src/xstate-runtime.js +866 -7
  64. package/src/xstate-runtime.ts +1397 -7
@@ -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
  } | {
@@ -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 = {
@@ -87,38 +85,28 @@ function outputString(event, field) {
87
85
  const value = playerOutput(event)?.[field];
88
86
  return isNonEmptyString(value) ? value : undefined;
89
87
  }
90
- function commitOutput(event) {
91
- const coderOutput = outputString(event, 'coderOutput');
92
- const latestCommit = outputString(event, 'latestCommit');
93
- const commitLines = coderOutput
94
- ?.split('\n')
95
- .filter((line) => line.startsWith('Commit: '));
96
- if (coderOutput === undefined ||
97
- latestCommit === undefined ||
98
- commitLines?.length !== 1 ||
99
- commitLines[0] !== `Commit: ${latestCommit}`) {
100
- return undefined;
101
- }
102
- return { coderOutput, latestCommit };
88
+ function hasCommittedOutput(event) {
89
+ return (outputString(event, 'coderOutput') !== undefined &&
90
+ outputString(event, 'latestCommit') !== undefined);
103
91
  }
104
92
  function isDirectCommit({ event }) {
105
93
  return (outputGuard(event, 'directCommit') &&
106
- commitOutput(event) !== undefined);
94
+ hasCommittedOutput(event));
107
95
  }
108
96
  function isIrCommit({ event }) {
109
97
  return (outputGuard(event, 'irCommit') &&
110
- commitOutput(event) !== undefined &&
98
+ hasCommittedOutput(event) &&
111
99
  outputString(event, 'irNumber') !== undefined &&
112
100
  outputString(event, 'irTask') !== undefined);
113
101
  }
114
102
  function isMoreTasks({ event }) {
115
103
  return (outputGuard(event, 'moreTasks') &&
116
- commitOutput(event) !== undefined &&
104
+ hasCommittedOutput(event) &&
117
105
  outputString(event, 'irTask') !== undefined);
118
106
  }
119
107
  function isFinalTask({ event }) {
120
108
  return (outputGuard(event, 'finalTask') &&
121
- commitOutput(event) !== undefined);
109
+ hasCommittedOutput(event));
122
110
  }
123
111
  function needsBossReply({ event }) {
124
112
  return (outputGuard(event, 'needsBossReply') &&
@@ -321,6 +309,7 @@ const machineSetup = setup({
321
309
  (event.questionId === undefined || event.questionId === 'runIrTask'),
322
310
  },
323
311
  actions: {
312
+ 'playbook.acceptedOutcome': (_args, _params) => undefined,
324
313
  startCoding: assign(({ context, event }) => {
325
314
  if (event.type !== 'START_CODE')
326
315
  return {};
@@ -487,17 +476,47 @@ export const codingMachine = machineSetup.createMachine({
487
476
  {
488
477
  guard: 'isDirectCommit',
489
478
  target: 'reviewFirstCommit',
490
- 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
+ ],
491
490
  },
492
491
  {
493
492
  guard: 'isIrCommit',
494
493
  target: 'reviewFirstCommit',
495
- 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
+ ],
496
505
  },
497
506
  {
498
507
  guard: 'needsBossReply',
499
508
  target: 'awaitBossReply',
500
- 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
+ ],
501
520
  },
502
521
  {
503
522
  target: 'failed',
@@ -575,17 +594,47 @@ export const codingMachine = machineSetup.createMachine({
575
594
  {
576
595
  guard: 'isMoreTasks',
577
596
  target: 'reviewIrTask',
578
- 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
+ ],
579
608
  },
580
609
  {
581
610
  guard: 'isFinalTask',
582
611
  target: 'reviewIrTask',
583
- 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
+ ],
584
623
  },
585
624
  {
586
625
  guard: 'needsBossReply',
587
626
  target: 'awaitBossReply',
588
- 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
+ ],
589
638
  },
590
639
  {
591
640
  target: 'failed',
@@ -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
 
@@ -246,36 +244,24 @@ function outputString(event: unknown, field: string): string | undefined {
246
244
  return isNonEmptyString(value) ? value : undefined;
247
245
  }
248
246
 
249
- function commitOutput(
250
- event: unknown,
251
- ): { readonly coderOutput: string; readonly latestCommit: string } | undefined {
252
- const coderOutput = outputString(event, 'coderOutput');
253
- const latestCommit = outputString(event, 'latestCommit');
254
- const commitLines = coderOutput
255
- ?.split('\n')
256
- .filter((line) => line.startsWith('Commit: '));
257
- if (
258
- coderOutput === undefined ||
259
- latestCommit === undefined ||
260
- commitLines?.length !== 1 ||
261
- commitLines[0] !== `Commit: ${latestCommit}`
262
- ) {
263
- return undefined;
264
- }
265
- return { coderOutput, latestCommit };
247
+ function hasCommittedOutput(event: unknown): boolean {
248
+ return (
249
+ outputString(event, 'coderOutput') !== undefined &&
250
+ outputString(event, 'latestCommit') !== undefined
251
+ );
266
252
  }
267
253
 
268
254
  function isDirectCommit({ event }: { event: unknown }): boolean {
269
255
  return (
270
256
  outputGuard(event, 'directCommit') &&
271
- commitOutput(event) !== undefined
257
+ hasCommittedOutput(event)
272
258
  );
273
259
  }
274
260
 
275
261
  function isIrCommit({ event }: { event: unknown }): boolean {
276
262
  return (
277
263
  outputGuard(event, 'irCommit') &&
278
- commitOutput(event) !== undefined &&
264
+ hasCommittedOutput(event) &&
279
265
  outputString(event, 'irNumber') !== undefined &&
280
266
  outputString(event, 'irTask') !== undefined
281
267
  );
@@ -284,7 +270,7 @@ function isIrCommit({ event }: { event: unknown }): boolean {
284
270
  function isMoreTasks({ event }: { event: unknown }): boolean {
285
271
  return (
286
272
  outputGuard(event, 'moreTasks') &&
287
- commitOutput(event) !== undefined &&
273
+ hasCommittedOutput(event) &&
288
274
  outputString(event, 'irTask') !== undefined
289
275
  );
290
276
  }
@@ -292,7 +278,7 @@ function isMoreTasks({ event }: { event: unknown }): boolean {
292
278
  function isFinalTask({ event }: { event: unknown }): boolean {
293
279
  return (
294
280
  outputGuard(event, 'finalTask') &&
295
- commitOutput(event) !== undefined
281
+ hasCommittedOutput(event)
296
282
  );
297
283
  }
298
284
 
@@ -553,6 +539,14 @@ const machineSetup = setup({
553
539
  (event.questionId === undefined || event.questionId === 'runIrTask'),
554
540
  },
555
541
  actions: {
542
+ 'playbook.acceptedOutcome': (
543
+ _args,
544
+ _params: {
545
+ readonly source: string;
546
+ readonly target: string;
547
+ readonly acceptedOutcome: string;
548
+ },
549
+ ) => undefined,
556
550
  startCoding: assign(({ context, event }) => {
557
551
  if (event.type !== 'START_CODE') return {};
558
552
  return {
@@ -726,17 +720,47 @@ export const codingMachine = machineSetup.createMachine({
726
720
  {
727
721
  guard: 'isDirectCommit',
728
722
  target: 'reviewFirstCommit',
729
- 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
+ ],
730
734
  },
731
735
  {
732
736
  guard: 'isIrCommit',
733
737
  target: 'reviewFirstCommit',
734
- 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
+ ],
735
749
  },
736
750
  {
737
751
  guard: 'needsBossReply',
738
752
  target: 'awaitBossReply',
739
- 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
+ ],
740
764
  },
741
765
  {
742
766
  target: 'failed',
@@ -814,17 +838,47 @@ export const codingMachine = machineSetup.createMachine({
814
838
  {
815
839
  guard: 'isMoreTasks',
816
840
  target: 'reviewIrTask',
817
- 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
+ ],
818
852
  },
819
853
  {
820
854
  guard: 'isFinalTask',
821
855
  target: 'reviewIrTask',
822
- 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
+ ],
823
867
  },
824
868
  {
825
869
  guard: 'needsBossReply',
826
870
  target: 'awaitBossReply',
827
- 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
+ ],
828
882
  },
829
883
  {
830
884
  target: 'failed',
@@ -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,7 +8,7 @@
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
11
+ // Compat: artifact schema 3 / runtime ABI 1
12
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']);
@@ -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,6 +81,7 @@ 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',
@@ -85,7 +89,7 @@ const runtimeSpec = {
85
89
  // time — a literal, never the loading engine's RUNTIME_ABI self-report,
86
90
  // which would follow whatever engine loads the module and make the
87
91
  // factory's skew check compare that engine with itself.
88
- compat: { artifactSchema: 2, runtimeAbi: 1 },
92
+ compat: { artifactSchema: 3, runtimeAbi: 1 },
89
93
  snapshotOptions: snapshotCodeOptions,
90
94
  entryEvent: {
91
95
  type: 'START_CODE',
@@ -105,9 +109,57 @@ const runtimeSpec = {
105
109
  label: 'Coder is implementing exactly one unfinished intent-record task.',
106
110
  },
107
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
+ },
108
159
  composePlayerPrompt: (input, promptIdentity) => composePlayerPrompt(input, promptIdentity),
109
160
  verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
110
161
  controlContextFields: ['phase'],
162
+ unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
111
163
  transitionEventFields: ['callerInput', 'answer', 'questionId'],
112
164
  };
113
165
  const createPlaybookRuntime = createXStatePlaybookRuntime(codingMachine, runtimeSpec);
@@ -8,20 +8,23 @@
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
11
+ // Compat: artifact schema 3 / runtime ABI 1
12
12
 
13
13
  import {
14
14
  createXStatePlaybookRuntime,
15
15
  snapshotJsonValue,
16
16
  type PlaybookPlayerInput,
17
+ type XStatePlaybookRuntimeFactory,
18
+ type XStatePlaybookRuntimeConstruction,
17
19
  type XStatePromptIdentity,
18
- type XStatePlaybookRuntimeSpec,
20
+ type XStatePlaybookRuntimeSpecV3,
19
21
  } from '@sublang/playbook/xstate-runtime';
20
22
  import {
21
23
  codingMachine,
22
24
  type CodingInput,
23
25
  type PlayerInput,
24
26
  } from './code.fsm.js';
27
+ import type { PlaybookHostConstructionCapabilities } from './playbook-captain.js';
25
28
  import type {
26
29
  CaptainCallOptions,
27
30
  CaptainResult,
@@ -75,6 +78,9 @@ export type {
75
78
  };
76
79
 
77
80
  export type CodePlaybookOptions = CodingInput;
81
+ export type CodePlaybookHostCapabilities =
82
+ PlaybookHostConstructionCapabilities &
83
+ XStatePlaybookRuntimeConstruction<CodePlaybookOptions, object>['hostCapabilities'];
78
84
 
79
85
  const OPTION_KEYS = new Set(['runResults']);
80
86
  const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
@@ -84,6 +90,9 @@ const CONTINUATION_PREAMBLE =
84
90
  const VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string> = new Set([
85
91
  'coderOutput',
86
92
  ]);
93
+ const UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string> = new Set([
94
+ 'reportedReviewFailure',
95
+ ]);
87
96
 
88
97
  function snapshotCodeOptions(value: unknown): CodePlaybookOptions {
89
98
  const captured = snapshotJsonValue(value, 'CODE runtime options');
@@ -163,6 +172,7 @@ function composePlayerPrompt(
163
172
  export const _internal = {
164
173
  composePlayerPrompt,
165
174
  VERBATIM_PAYLOAD_FIELDS,
175
+ UNFINISHED_FINAL_STATE_IDS,
166
176
  };
167
177
 
168
178
  const runtimeSpec = {
@@ -171,7 +181,7 @@ const runtimeSpec = {
171
181
  // time — a literal, never the loading engine's RUNTIME_ABI self-report,
172
182
  // which would follow whatever engine loads the module and make the
173
183
  // factory's skew check compare that engine with itself.
174
- compat: { artifactSchema: 2, runtimeAbi: 1 },
184
+ compat: { artifactSchema: 3, runtimeAbi: 1 },
175
185
  snapshotOptions: snapshotCodeOptions,
176
186
  entryEvent: {
177
187
  type: 'START_CODE',
@@ -192,16 +202,75 @@ const runtimeSpec = {
192
202
  label: 'Coder is implementing exactly one unfinished intent-record task.',
193
203
  },
194
204
  },
205
+ outcomeAuthority: {
206
+ governedPlayerStates: {
207
+ runFirstPhase: {
208
+ directCommit: {
209
+ fields: {
210
+ coderOutput: 'presentation',
211
+ latestCommit: 'effect',
212
+ },
213
+ repositoryDisposition: 'one-descendant-commit',
214
+ },
215
+ irCommit: {
216
+ fields: {
217
+ coderOutput: 'presentation',
218
+ latestCommit: 'effect',
219
+ irNumber: 'semantic',
220
+ irTask: 'semantic',
221
+ },
222
+ repositoryDisposition: 'one-descendant-commit',
223
+ },
224
+ needsBossReply: {
225
+ fields: { question: 'presentation' },
226
+ repositoryDisposition: 'deferred',
227
+ },
228
+ },
229
+ runIrTask: {
230
+ moreTasks: {
231
+ fields: {
232
+ coderOutput: 'presentation',
233
+ latestCommit: 'effect',
234
+ irTask: 'semantic',
235
+ },
236
+ repositoryDisposition: 'one-descendant-commit',
237
+ },
238
+ finalTask: {
239
+ fields: {
240
+ coderOutput: 'presentation',
241
+ latestCommit: 'effect',
242
+ },
243
+ repositoryDisposition: 'one-descendant-commit',
244
+ },
245
+ needsBossReply: {
246
+ fields: { question: 'presentation' },
247
+ repositoryDisposition: 'deferred',
248
+ },
249
+ },
250
+ },
251
+ },
195
252
  composePlayerPrompt: (
196
253
  input: PlaybookPlayerInput,
197
254
  promptIdentity: XStatePromptIdentity,
198
255
  ) => composePlayerPrompt(input as PlayerInput, promptIdentity),
199
256
  verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
200
257
  controlContextFields: ['phase'],
258
+ unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
201
259
  transitionEventFields: ['callerInput', 'answer', 'questionId'],
202
- } satisfies XStatePlaybookRuntimeSpec<CodePlaybookOptions>;
260
+ } satisfies XStatePlaybookRuntimeSpecV3<CodePlaybookOptions>;
203
261
 
204
- const createPlaybookRuntime: PlaybookRuntimeFactory<CodePlaybookOptions> =
205
- createXStatePlaybookRuntime(codingMachine, runtimeSpec);
262
+ const createPlaybookRuntime: XStatePlaybookRuntimeFactory<
263
+ XStatePlaybookRuntimeConstruction<
264
+ CodePlaybookOptions,
265
+ CodePlaybookHostCapabilities
266
+ >,
267
+ 3
268
+ > = createXStatePlaybookRuntime<
269
+ CodePlaybookOptions,
270
+ CodePlaybookHostCapabilities
271
+ >(
272
+ codingMachine,
273
+ runtimeSpec,
274
+ );
206
275
 
207
276
  export default createPlaybookRuntime;
@@ -1,4 +1,4 @@
1
- import { type PlaybookRuntime } from './code.playbook.js';
1
+ import { type CodePlaybookHostCapabilities, type PlaybookRuntime } from './code.playbook.js';
2
2
  export interface PlaybookSummaryPolicy {
3
3
  stateCountLabels: Readonly<Record<string, string>>;
4
4
  copyPasteGuardNames: readonly string[];
@@ -12,12 +12,19 @@ export interface CodePlaybookRegistryEntry {
12
12
  id: 'code';
13
13
  command: 'code';
14
14
  intent: string;
15
- artifactSchema: 2;
15
+ artifactSchema: 3;
16
+ runtimeProfile: {
17
+ readonly kind: 'shared-factory';
18
+ readonly compat: {
19
+ readonly artifactSchema: 3;
20
+ readonly runtimeAbi: number;
21
+ };
22
+ };
16
23
  requiredRoleIds: readonly ['coder'];
17
24
  concurrentRoleSets: readonly [];
18
25
  summaryPolicy: PlaybookSummaryPolicy;
19
26
  validateOptions(optionSlice: unknown): CodeOptions;
20
- createRuntime(options: CodeOptions): PlaybookRuntime;
27
+ createRuntime(options: CodeOptions, hostCapabilities: CodePlaybookHostCapabilities): PlaybookRuntime;
21
28
  }
22
29
  export declare const codeStateCountLabels: {};
23
30
  export declare const codeCopyPasteGuardNames: readonly ["directCommit", "irCommit", "moreTasks", "finalTask"];