@sublang/playbook 0.9.0 → 1.3.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 (51) hide show
  1. package/README.md +190 -151
  2. package/package.json +50 -6
  3. package/reference/sdlc/captain.md +102 -0
  4. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +227 -0
  5. package/reference/sdlc/captain.playbook/captain.fsm.js +628 -0
  6. package/reference/sdlc/captain.playbook/captain.fsm.ts +851 -0
  7. package/reference/sdlc/captain.playbook/captain.gears.md +60 -0
  8. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +23 -0
  9. package/reference/sdlc/captain.playbook/captain.playbook.js +1053 -0
  10. package/reference/sdlc/captain.playbook/captain.playbook.ts +1144 -0
  11. package/reference/sdlc/code.playbook/bin/playbook.js +158 -12
  12. package/reference/sdlc/code.playbook/bin/run.js +999 -0
  13. package/reference/sdlc/code.playbook/code.fsm.d.ts +11 -4
  14. package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +2 -2
  15. package/reference/sdlc/code.playbook/code.fsm.introspect.js +1 -1
  16. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +6 -6
  17. package/reference/sdlc/code.playbook/code.fsm.js +334 -102
  18. package/reference/sdlc/code.playbook/code.fsm.ts +467 -180
  19. package/reference/sdlc/code.playbook/code.gears.md +11 -10
  20. package/reference/sdlc/code.playbook/code.playbook.d.ts +16 -19
  21. package/reference/sdlc/code.playbook/code.playbook.js +199 -488
  22. package/reference/sdlc/code.playbook/code.playbook.ts +327 -566
  23. package/reference/sdlc/code.playbook/code.registry.d.ts +0 -3
  24. package/reference/sdlc/code.playbook/code.registry.js +0 -3
  25. package/reference/sdlc/code.playbook/code.registry.ts +0 -6
  26. package/reference/sdlc/code.playbook/playbook-captain.d.ts +9 -4
  27. package/reference/sdlc/code.playbook/playbook-captain.js +889 -210
  28. package/reference/sdlc/code.playbook/playbook-captain.ts +1136 -257
  29. package/reference/sdlc/code.playbook/playbook.config.template.yaml +21 -0
  30. package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +396 -0
  31. package/reference/sdlc/discuss.playbook/discuss.fsm.js +2066 -0
  32. package/reference/sdlc/discuss.playbook/discuss.fsm.ts +2464 -0
  33. package/reference/sdlc/discuss.playbook/discuss.gears.md +251 -0
  34. package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +113 -0
  35. package/reference/sdlc/discuss.playbook/discuss.playbook.js +1514 -0
  36. package/reference/sdlc/discuss.playbook/discuss.playbook.ts +1926 -0
  37. package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +58 -0
  38. package/reference/sdlc/discuss.playbook/discuss.registry.js +97 -0
  39. package/reference/sdlc/discuss.playbook/discuss.registry.ts +153 -0
  40. package/slc/gears2fsm.md +557 -57
  41. package/slc/link.md +1165 -89
  42. package/slc/optimize.md +92 -0
  43. package/slc/text2gears.md +255 -7
  44. package/src/runtime.d.ts +146 -3
  45. package/src/runtime.ts +201 -2
  46. package/src/xstate-playbook-runtime.d.ts +201 -0
  47. package/src/xstate-playbook-runtime.js +2058 -0
  48. package/src/xstate-playbook-runtime.ts +2792 -0
  49. package/src/xstate-runtime.d.ts +95 -0
  50. package/src/xstate-runtime.js +1258 -0
  51. package/src/xstate-runtime.ts +1816 -0
@@ -4,9 +4,9 @@
4
4
  // XState v5's generics do not flow through helpers declared outside the
5
5
  // machine (e.g., shared guard/action factories, the `readyEvents` block),
6
6
  // so strict checking emits structural-mismatch noise without surfacing real
7
- // bugs. The runner overrides `captain` via `.provide({ actors: { ... } })`
7
+ // bugs. The runner overrides `player` via `.provide({ actors: { ... } })`
8
8
  // and re-asserts the public surface (`CodingContext`, `CodingEvent`,
9
- // `CaptainInput`, `CaptainOutput`) at the boundary.
9
+ // `PlayerInput`, `PlayerOutput`) at the boundary.
10
10
  // @ts-nocheck
11
11
 
12
12
  import { assign, fromPromise, setup } from 'xstate';
@@ -43,6 +43,7 @@ type AfterReview = 'continueIr' | 'summarizeSpecs' | 'done';
43
43
  type ResumableStateId = Exclude<JumpableStateId, 'ready' | 'failed'>;
44
44
 
45
45
  type PendingBossQuestion = {
46
+ questionId: ResumableStateId;
46
47
  resumeStateId: ResumableStateId;
47
48
  sourceItem: string;
48
49
  player: Player;
@@ -50,7 +51,7 @@ type PendingBossQuestion = {
50
51
  };
51
52
 
52
53
  export type CaptainInput = {
53
- player: Player;
54
+ stateId: ResumableStateId;
54
55
  sourceItem: string;
55
56
  prompt: string;
56
57
  result: Record<string, string>;
@@ -78,6 +79,10 @@ export type CaptainInput = {
78
79
  bossReply?: string;
79
80
  };
80
81
 
82
+ export type PlayerInput = CaptainInput & {
83
+ player: Player;
84
+ };
85
+
81
86
  export type CaptainOutput = {
82
87
  guard: string;
83
88
  irNumber?: string;
@@ -89,6 +94,8 @@ export type CaptainOutput = {
89
94
  [k: string]: unknown;
90
95
  };
91
96
 
97
+ export type PlayerOutput = CaptainOutput;
98
+
92
99
  export type CodingInput = {
93
100
  intent?: string;
94
101
  irNumber?: string;
@@ -121,7 +128,11 @@ export type CodingEvent =
121
128
  intent?: string;
122
129
  irNumber?: string;
123
130
  }
124
- | { type: 'BOSS_REPLY'; answer: string };
131
+ | {
132
+ type: 'BOSS_REPLY';
133
+ answer: string;
134
+ questionId?: ResumableStateId;
135
+ };
125
136
 
126
137
  const jumpableStateIds = [
127
138
  'ready',
@@ -169,39 +180,113 @@ const resumableStateIds = [
169
180
  'commitJoint',
170
181
  ] as const satisfies readonly ResumableStateId[];
171
182
 
183
+ const stateDescriptions = {
184
+ ready: 'Idle hub: waits for Boss to start or resume a coding sub-procedure.',
185
+ awaitBossReply: 'Waiting for Boss to answer a player question.',
186
+ planAndImplement:
187
+ 'CODE-1: Coder assesses a Boss intent and either implements a single-commit change or drafts an IR.',
188
+ respondToReview: 'CODE-2: Coder addresses or challenges Reviewer findings.',
189
+ continueIr:
190
+ 'CODE-3: Coder continues an IR after the previous task or IR draft passed review.',
191
+ summarizeSpecs:
192
+ 'CODE-4: Coder summarizes a completed IR into minimal spec items.',
193
+ reviewBossCommitSpecs:
194
+ 'CODE-5: Reviewer reviews a Boss-intent commit whose changes are only in @specs/{user,dev,test}/.',
195
+ reviewBossCommitCode:
196
+ 'CODE-6: Reviewer reviews a Boss-intent commit whose changes are only outside @specs/{user,dev,test}/.',
197
+ reviewBossCommitMixed:
198
+ 'CODE-7: Reviewer reviews a Boss-intent commit whose changes span both @specs/{user,dev,test}/ and other files.',
199
+ reviewIrTaskCommitSpecs:
200
+ 'CODE-8: Reviewer reviews an IR-task commit whose changes are only in @specs/{user,dev,test}/.',
201
+ reviewIrTaskCommitCode:
202
+ 'CODE-9: Reviewer reviews an IR-task commit whose changes are only outside @specs/{user,dev,test}/.',
203
+ reviewIrTaskCommitMixed:
204
+ 'CODE-10: Reviewer reviews an IR-task commit whose changes span both @specs/{user,dev,test}/ and other files.',
205
+ reviewChangesSpecs:
206
+ 'CODE-11: Reviewer reviews uncommitted Coder changes that touch only @specs/{user,dev,test}/ with no accompanying rebuttals.',
207
+ reviewChangesCode:
208
+ 'CODE-12: Reviewer reviews uncommitted Coder changes that touch only files outside @specs/{user,dev,test}/ with no accompanying rebuttals.',
209
+ reviewChangesMixed:
210
+ 'CODE-13: Reviewer reviews uncommitted Coder changes that touch both @specs/{user,dev,test}/ and other files with no accompanying rebuttals.',
211
+ reviewChangesAndChallengesSpecs:
212
+ 'CODE-15: Reviewer reviews uncommitted Coder changes that touch only @specs/{user,dev,test}/ and adjudicates accompanying rebuttals in one round.',
213
+ reviewChangesAndChallengesCode:
214
+ 'CODE-16: Reviewer reviews uncommitted Coder changes that touch only files outside @specs/{user,dev,test}/ and adjudicates accompanying rebuttals in one round.',
215
+ reviewChangesAndChallengesMixed:
216
+ 'CODE-17: Reviewer reviews uncommitted Coder changes that touch both @specs/{user,dev,test}/ and other files and adjudicates accompanying rebuttals in one round.',
217
+ adjudicateChallenges:
218
+ 'CODE-14: Reviewer adjudicates Coder rebuttals against the prior review when Coder produced no code edits this round.',
219
+ commitCoderInitial:
220
+ 'CODE-18: Committer commits Coder Initial Changes when Reviewer has not played since the last commit.',
221
+ commitJoint:
222
+ 'CODE-19: Committer commits changes when both Coder and Reviewer have played since the last commit.',
223
+ failed:
224
+ 'Captures the last Captain error so the runner can report it. Boss may resume from here.',
225
+ done: 'The selected coding workflow has completed.',
226
+ } as const satisfies Record<
227
+ JumpableStateId | 'awaitBossReply' | 'done',
228
+ string
229
+ >;
230
+
231
+ const playbookMeta = <StateId extends keyof typeof stateDescriptions>(
232
+ stateId: StateId,
233
+ ) => ({
234
+ playbook: {
235
+ stateId,
236
+ description: stateDescriptions[stateId],
237
+ },
238
+ });
239
+
172
240
  const outputOf = (event: unknown): CaptainOutput | undefined =>
173
241
  (event as { output?: CaptainOutput }).output;
174
242
 
175
- const guardIs = (guard: string) =>
176
- ({ event }: { event: unknown }) => outputOf(event)?.guard === guard;
243
+ const guardIs =
244
+ (guard: string) =>
245
+ ({ event }: { event: unknown }) =>
246
+ outputOf(event)?.guard === guard;
177
247
 
178
- const guardAndOrigin = (guard: string, origin: ChangeOrigin) =>
248
+ const guardAndOrigin =
249
+ (guard: string, origin: ChangeOrigin) =>
179
250
  ({ context, event }: { context: CodingContext; event: unknown }) =>
180
251
  outputOf(event)?.guard === guard && context.changeOrigin === origin;
181
252
 
182
- const acceptedAfter = (subject: ReviewSubject) =>
253
+ const acceptedAfter =
254
+ (subject: ReviewSubject) =>
183
255
  ({ context, event }: { context: CodingContext; event: unknown }) =>
184
256
  outputOf(event)?.guard === 'accepted' && context.reviewSubject === subject;
185
257
 
186
- const noFindingsAfter = (afterReview: AfterReview) =>
258
+ const noFindingsAfter =
259
+ (afterReview: AfterReview) =>
187
260
  ({ context, event }: { context: CodingContext; event: unknown }) =>
188
- outputOf(event)?.guard === 'noFindings' && context.afterReview === afterReview;
261
+ outputOf(event)?.guard === 'noFindings' &&
262
+ context.afterReview === afterReview;
189
263
 
190
264
  const rememberCaptainOutput = assign({
191
265
  lastResult: ({ event }: { event: unknown }) => outputOf(event),
192
266
  lastError: () => undefined,
193
267
  irNumber: ({ context, event }: { context: CodingContext; event: unknown }) =>
194
268
  outputOf(event)?.irNumber ?? context.irNumber,
195
- taskDescription: ({ context, event }: { context: CodingContext; event: unknown }) =>
196
- outputOf(event)?.taskDescription ?? context.taskDescription,
269
+ taskDescription: ({
270
+ context,
271
+ event,
272
+ }: {
273
+ context: CodingContext;
274
+ event: unknown;
275
+ }) => outputOf(event)?.taskDescription ?? context.taskDescription,
197
276
  reviews: ({ context, event }: { context: CodingContext; event: unknown }) =>
198
277
  outputOf(event)?.reviews ?? context.reviews,
199
- challenges: ({ context, event }: { context: CodingContext; event: unknown }) =>
200
- outputOf(event)?.challenges ?? context.challenges,
278
+ challenges: ({
279
+ context,
280
+ event,
281
+ }: {
282
+ context: CodingContext;
283
+ event: unknown;
284
+ }) => outputOf(event)?.challenges ?? context.challenges,
201
285
  });
202
286
 
203
287
  const rememberCaptainError = assign({
204
- lastError: ({ event }: { event: unknown }) => (event as { error?: unknown }).error,
288
+ lastError: ({ event }: { event: unknown }) =>
289
+ (event as { error?: unknown }).error,
205
290
  });
206
291
 
207
292
  const rememberEmptyBossReplyError = assign({
@@ -215,10 +300,10 @@ const rememberBossInput = assign({
215
300
  (event as { irNumber?: string }).irNumber ?? context.irNumber,
216
301
  });
217
302
 
218
- type CaptainInputFactory = (context: Partial<CodingContext>) => CaptainInput;
303
+ type PlayerInputFactory = (context: Partial<CodingContext>) => PlayerInput;
219
304
 
220
305
  const needsBossReplyDescription =
221
- "The player's prose surfaces a clarifying question for Boss that the player cannot answer alone. Output shall include `question: <verbatim question text from the player's prose>`.";
306
+ "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>`.";
222
307
 
223
308
  const bossReplyInputFields = (context: Partial<CodingContext>) => ({
224
309
  pendingBossQuestion: context.pendingBossQuestion,
@@ -244,9 +329,15 @@ const captainStateMetadata = {
244
329
  reviewChangesSpecs: { sourceItem: 'CODE-11', player: 'Reviewer' },
245
330
  reviewChangesCode: { sourceItem: 'CODE-12', player: 'Reviewer' },
246
331
  reviewChangesMixed: { sourceItem: 'CODE-13', player: 'Reviewer' },
247
- reviewChangesAndChallengesSpecs: { sourceItem: 'CODE-15', player: 'Reviewer' },
332
+ reviewChangesAndChallengesSpecs: {
333
+ sourceItem: 'CODE-15',
334
+ player: 'Reviewer',
335
+ },
248
336
  reviewChangesAndChallengesCode: { sourceItem: 'CODE-16', player: 'Reviewer' },
249
- reviewChangesAndChallengesMixed: { sourceItem: 'CODE-17', player: 'Reviewer' },
337
+ reviewChangesAndChallengesMixed: {
338
+ sourceItem: 'CODE-17',
339
+ player: 'Reviewer',
340
+ },
250
341
  adjudicateChallenges: { sourceItem: 'CODE-14', player: 'Reviewer' },
251
342
  commitCoderInitial: { sourceItem: 'CODE-18', player: 'Committer' },
252
343
  commitJoint: { sourceItem: 'CODE-19', player: 'Committer' },
@@ -255,7 +346,8 @@ const captainStateMetadata = {
255
346
  { sourceItem: string; player: Player }
256
347
  >;
257
348
 
258
- const planAndImplementInput: CaptainInputFactory = (context) => ({
349
+ const planAndImplementInput: PlayerInputFactory = (context) => ({
350
+ stateId: 'planAndImplement',
259
351
  player: 'Coder',
260
352
  sourceItem: 'CODE-1',
261
353
  intent: context.intent,
@@ -275,7 +367,8 @@ const planAndImplementInput: CaptainInputFactory = (context) => ({
275
367
  }),
276
368
  });
277
369
 
278
- const continueIrInput: CaptainInputFactory = (context) => ({
370
+ const continueIrInput: PlayerInputFactory = (context) => ({
371
+ stateId: 'continueIr',
279
372
  player: 'Coder',
280
373
  sourceItem: 'CODE-3',
281
374
  irNumber: context.irNumber,
@@ -293,7 +386,8 @@ const continueIrInput: CaptainInputFactory = (context) => ({
293
386
  }),
294
387
  });
295
388
 
296
- const summarizeSpecsInput: CaptainInputFactory = (context) => ({
389
+ const summarizeSpecsInput: PlayerInputFactory = (context) => ({
390
+ stateId: 'summarizeSpecs',
297
391
  player: 'Coder',
298
392
  sourceItem: 'CODE-4',
299
393
  irNumber: context.irNumber,
@@ -329,6 +423,7 @@ const setPendingBossQuestion = (resumeStateId: ResumableStateId) =>
329
423
  }) => {
330
424
  const input = captainStateMetadata[resumeStateId];
331
425
  return {
426
+ questionId: resumeStateId,
332
427
  resumeStateId,
333
428
  sourceItem: input.sourceItem,
334
429
  player: input.player,
@@ -351,7 +446,9 @@ const clearBossReplyContext = assign({
351
446
  const actionsArray = (actions: unknown) =>
352
447
  actions === undefined ? [] : Array.isArray(actions) ? actions : [actions];
353
448
 
354
- const withClearBossReplyContext = <T extends { actions?: unknown }>(transition: T): T => ({
449
+ const withClearBossReplyContext = <T extends { actions?: unknown }>(
450
+ transition: T,
451
+ ): T => ({
355
452
  ...transition,
356
453
  actions: [clearBossReplyContext, ...actionsArray(transition.actions)],
357
454
  });
@@ -371,9 +468,17 @@ const bossInterrupts = (
371
468
  const resumableStates = (ids: readonly ResumableStateId[]) =>
372
469
  ids.map((id) => ({
373
470
  target: `#${id}` as const,
374
- guard: ({ context, event }: { context: CodingContext; event: CodingEvent }) =>
471
+ guard: ({
472
+ context,
473
+ event,
474
+ }: {
475
+ context: CodingContext;
476
+ event: CodingEvent;
477
+ }) =>
375
478
  event.type === 'BOSS_REPLY' &&
376
- context.pendingBossQuestion?.resumeStateId === id,
479
+ context.pendingBossQuestion?.resumeStateId === id &&
480
+ (event.questionId === undefined ||
481
+ event.questionId === context.pendingBossQuestion.questionId),
377
482
  reenter: true,
378
483
  actions: rememberBossReply,
379
484
  }));
@@ -451,8 +556,8 @@ const awaitBossReplyEvents = {
451
556
  ],
452
557
  };
453
558
 
454
- const captainPlaceholder = fromPromise<CaptainOutput, CaptainInput>(async () => {
455
- throw new Error('captain actor must be provided by the runner');
559
+ const playerPlaceholder = fromPromise<PlayerOutput, PlayerInput>(async () => {
560
+ throw new Error('player actor must be provided by the runner');
456
561
  });
457
562
 
458
563
  export const codingMachine = setup({
@@ -462,7 +567,7 @@ export const codingMachine = setup({
462
567
  input: CodingInput | undefined;
463
568
  },
464
569
  actors: {
465
- captain: captainPlaceholder,
570
+ player: playerPlaceholder,
466
571
  },
467
572
  }).createMachine({
468
573
  id: 'coding',
@@ -480,23 +585,28 @@ export const codingMachine = setup({
480
585
  states: {
481
586
  ready: {
482
587
  id: 'ready',
483
- description: 'Idle hub: waits for Boss to start or resume a coding sub-procedure.',
588
+ description: stateDescriptions.ready,
589
+ meta: playbookMeta('ready'),
590
+ tags: ['playbook.parked'],
484
591
  on: readyEvents,
485
592
  },
486
593
 
487
594
  awaitBossReply: {
488
595
  id: 'awaitBossReply',
489
- description: 'Waiting for Boss to answer a player question.',
596
+ description: stateDescriptions.awaitBossReply,
597
+ meta: playbookMeta('awaitBossReply'),
598
+ tags: ['playbook.parked'],
490
599
  on: awaitBossReplyEvents,
491
600
  },
492
601
 
493
602
  planAndImplement: {
494
603
  id: 'planAndImplement',
495
- description:
496
- 'CODE-1: Coder assesses a Boss intent and either implements a single-commit change or drafts an IR.',
604
+ description: stateDescriptions.planAndImplement,
605
+ meta: playbookMeta('planAndImplement'),
606
+ tags: ['playbook.busy'],
497
607
  invoke: {
498
- src: 'captain',
499
- input: ({ context }): CaptainInput => planAndImplementInput(context),
608
+ src: 'player',
609
+ input: ({ context }): PlayerInput => planAndImplementInput(context),
500
610
  onDone: withNeedsBossReplyTransition('planAndImplement', [
501
611
  {
502
612
  guard: guardIs('singleCommitReady'),
@@ -529,10 +639,13 @@ export const codingMachine = setup({
529
639
 
530
640
  respondToReview: {
531
641
  id: 'respondToReview',
532
- description: 'CODE-2: Coder addresses or challenges Reviewer findings.',
642
+ description: stateDescriptions.respondToReview,
643
+ meta: playbookMeta('respondToReview'),
644
+ tags: ['playbook.busy'],
533
645
  invoke: {
534
- src: 'captain',
535
- input: ({ context }): CaptainInput => ({
646
+ src: 'player',
647
+ input: ({ context }): PlayerInput => ({
648
+ stateId: 'respondToReview',
536
649
  player: 'Coder',
537
650
  sourceItem: 'CODE-2',
538
651
  ...bossReplyInputFields(context),
@@ -633,11 +746,12 @@ export const codingMachine = setup({
633
746
 
634
747
  continueIr: {
635
748
  id: 'continueIr',
636
- description:
637
- 'CODE-3: Coder continues an IR after the previous task or IR draft passed review.',
749
+ description: stateDescriptions.continueIr,
750
+ meta: playbookMeta('continueIr'),
751
+ tags: ['playbook.busy'],
638
752
  invoke: {
639
- src: 'captain',
640
- input: ({ context }): CaptainInput => continueIrInput(context),
753
+ src: 'player',
754
+ input: ({ context }): PlayerInput => continueIrInput(context),
641
755
  onDone: withNeedsBossReplyTransition('continueIr', [
642
756
  {
643
757
  guard: guardIs('taskReady'),
@@ -669,10 +783,12 @@ export const codingMachine = setup({
669
783
 
670
784
  summarizeSpecs: {
671
785
  id: 'summarizeSpecs',
672
- description: 'CODE-4: Coder summarizes a completed IR into minimal spec items.',
786
+ description: stateDescriptions.summarizeSpecs,
787
+ meta: playbookMeta('summarizeSpecs'),
788
+ tags: ['playbook.busy'],
673
789
  invoke: {
674
- src: 'captain',
675
- input: ({ context }): CaptainInput => summarizeSpecsInput(context),
790
+ src: 'player',
791
+ input: ({ context }): PlayerInput => summarizeSpecsInput(context),
676
792
  onDone: withNeedsBossReplyTransition('summarizeSpecs', [
677
793
  {
678
794
  guard: guardIs('specsReady'),
@@ -698,11 +814,13 @@ export const codingMachine = setup({
698
814
 
699
815
  reviewBossCommitSpecs: {
700
816
  id: 'reviewBossCommitSpecs',
701
- description:
702
- 'CODE-5: Reviewer reviews a Boss-intent commit whose changes are only in @specs/{user,dev,test}/.',
817
+ description: stateDescriptions.reviewBossCommitSpecs,
818
+ meta: playbookMeta('reviewBossCommitSpecs'),
819
+ tags: ['playbook.busy'],
703
820
  invoke: {
704
- src: 'captain',
705
- input: ({ context }): CaptainInput => ({
821
+ src: 'player',
822
+ input: ({ context }): PlayerInput => ({
823
+ stateId: 'reviewBossCommitSpecs',
706
824
  player: 'Reviewer',
707
825
  sourceItem: 'CODE-5',
708
826
  ...bossReplyInputFields(context),
@@ -731,13 +849,28 @@ export const codingMachine = setup({
731
849
  },
732
850
  }),
733
851
  onDone: withNeedsBossReplyTransition('reviewBossCommitSpecs', [
734
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
735
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
736
- { guard: noFindingsAfter('done'), target: '#done', actions: rememberCaptainOutput },
852
+ {
853
+ guard: noFindingsAfter('continueIr'),
854
+ target: '#continueIr',
855
+ actions: rememberCaptainOutput,
856
+ },
857
+ {
858
+ guard: noFindingsAfter('summarizeSpecs'),
859
+ target: '#summarizeSpecs',
860
+ actions: rememberCaptainOutput,
861
+ },
862
+ {
863
+ guard: noFindingsAfter('done'),
864
+ target: '#done',
865
+ actions: rememberCaptainOutput,
866
+ },
737
867
  {
738
868
  guard: guardIs('hasFindings'),
739
869
  target: '#respondToReview',
740
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
870
+ actions: [
871
+ rememberCaptainOutput,
872
+ assign({ reviewSubject: () => 'commit' as const }),
873
+ ],
741
874
  },
742
875
  ]),
743
876
  onError: captainError,
@@ -746,11 +879,13 @@ export const codingMachine = setup({
746
879
 
747
880
  reviewBossCommitCode: {
748
881
  id: 'reviewBossCommitCode',
749
- description:
750
- 'CODE-6: Reviewer reviews a Boss-intent commit whose changes are only outside @specs/{user,dev,test}/.',
882
+ description: stateDescriptions.reviewBossCommitCode,
883
+ meta: playbookMeta('reviewBossCommitCode'),
884
+ tags: ['playbook.busy'],
751
885
  invoke: {
752
- src: 'captain',
753
- input: ({ context }): CaptainInput => ({
886
+ src: 'player',
887
+ input: ({ context }): PlayerInput => ({
888
+ stateId: 'reviewBossCommitCode',
754
889
  player: 'Reviewer',
755
890
  sourceItem: 'CODE-6',
756
891
  ...bossReplyInputFields(context),
@@ -773,13 +908,28 @@ export const codingMachine = setup({
773
908
  },
774
909
  }),
775
910
  onDone: withNeedsBossReplyTransition('reviewBossCommitCode', [
776
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
777
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
778
- { guard: noFindingsAfter('done'), target: '#done', actions: rememberCaptainOutput },
911
+ {
912
+ guard: noFindingsAfter('continueIr'),
913
+ target: '#continueIr',
914
+ actions: rememberCaptainOutput,
915
+ },
916
+ {
917
+ guard: noFindingsAfter('summarizeSpecs'),
918
+ target: '#summarizeSpecs',
919
+ actions: rememberCaptainOutput,
920
+ },
921
+ {
922
+ guard: noFindingsAfter('done'),
923
+ target: '#done',
924
+ actions: rememberCaptainOutput,
925
+ },
779
926
  {
780
927
  guard: guardIs('hasFindings'),
781
928
  target: '#respondToReview',
782
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
929
+ actions: [
930
+ rememberCaptainOutput,
931
+ assign({ reviewSubject: () => 'commit' as const }),
932
+ ],
783
933
  },
784
934
  ]),
785
935
  onError: captainError,
@@ -788,11 +938,13 @@ export const codingMachine = setup({
788
938
 
789
939
  reviewBossCommitMixed: {
790
940
  id: 'reviewBossCommitMixed',
791
- description:
792
- 'CODE-7: Reviewer reviews a Boss-intent commit whose changes span both @specs/{user,dev,test}/ and other files.',
941
+ description: stateDescriptions.reviewBossCommitMixed,
942
+ meta: playbookMeta('reviewBossCommitMixed'),
943
+ tags: ['playbook.busy'],
793
944
  invoke: {
794
- src: 'captain',
795
- input: ({ context }): CaptainInput => ({
945
+ src: 'player',
946
+ input: ({ context }): PlayerInput => ({
947
+ stateId: 'reviewBossCommitMixed',
796
948
  player: 'Reviewer',
797
949
  sourceItem: 'CODE-7',
798
950
  ...bossReplyInputFields(context),
@@ -800,6 +952,8 @@ export const codingMachine = setup({
800
952
  prompt: [
801
953
  'Review the latest commit.',
802
954
  'Refer to the commit message.',
955
+ 'Flag any issues or improvements (numbered; no duplication).',
956
+ "Think thoroughly — don't just approve or reject.",
803
957
  'Verify any affected spec items are:',
804
958
  '',
805
959
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -808,8 +962,6 @@ export const codingMachine = setup({
808
962
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
809
963
  '',
810
964
  'Flag anything missing, redundant, over-specified, or under-specified.',
811
- 'Flag any issues or improvements (numbered; no duplication).',
812
- "Think thoroughly — don't just approve or reject.",
813
965
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
814
966
  'Verify @specs/map.md reflects the changes.',
815
967
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -823,13 +975,28 @@ export const codingMachine = setup({
823
975
  },
824
976
  }),
825
977
  onDone: withNeedsBossReplyTransition('reviewBossCommitMixed', [
826
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
827
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
828
- { guard: noFindingsAfter('done'), target: '#done', actions: rememberCaptainOutput },
978
+ {
979
+ guard: noFindingsAfter('continueIr'),
980
+ target: '#continueIr',
981
+ actions: rememberCaptainOutput,
982
+ },
983
+ {
984
+ guard: noFindingsAfter('summarizeSpecs'),
985
+ target: '#summarizeSpecs',
986
+ actions: rememberCaptainOutput,
987
+ },
988
+ {
989
+ guard: noFindingsAfter('done'),
990
+ target: '#done',
991
+ actions: rememberCaptainOutput,
992
+ },
829
993
  {
830
994
  guard: guardIs('hasFindings'),
831
995
  target: '#respondToReview',
832
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
996
+ actions: [
997
+ rememberCaptainOutput,
998
+ assign({ reviewSubject: () => 'commit' as const }),
999
+ ],
833
1000
  },
834
1001
  ]),
835
1002
  onError: captainError,
@@ -838,11 +1005,13 @@ export const codingMachine = setup({
838
1005
 
839
1006
  reviewIrTaskCommitSpecs: {
840
1007
  id: 'reviewIrTaskCommitSpecs',
841
- description:
842
- 'CODE-8: Reviewer reviews an IR-task commit whose changes are only in @specs/{user,dev,test}/.',
1008
+ description: stateDescriptions.reviewIrTaskCommitSpecs,
1009
+ meta: playbookMeta('reviewIrTaskCommitSpecs'),
1010
+ tags: ['playbook.busy'],
843
1011
  invoke: {
844
- src: 'captain',
845
- input: ({ context }): CaptainInput => ({
1012
+ src: 'player',
1013
+ input: ({ context }): PlayerInput => ({
1014
+ stateId: 'reviewIrTaskCommitSpecs',
846
1015
  player: 'Reviewer',
847
1016
  sourceItem: 'CODE-8',
848
1017
  ...bossReplyInputFields(context),
@@ -872,13 +1041,28 @@ export const codingMachine = setup({
872
1041
  },
873
1042
  }),
874
1043
  onDone: withNeedsBossReplyTransition('reviewIrTaskCommitSpecs', [
875
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
876
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
877
- { guard: noFindingsAfter('done'), target: '#done', actions: rememberCaptainOutput },
1044
+ {
1045
+ guard: noFindingsAfter('continueIr'),
1046
+ target: '#continueIr',
1047
+ actions: rememberCaptainOutput,
1048
+ },
1049
+ {
1050
+ guard: noFindingsAfter('summarizeSpecs'),
1051
+ target: '#summarizeSpecs',
1052
+ actions: rememberCaptainOutput,
1053
+ },
1054
+ {
1055
+ guard: noFindingsAfter('done'),
1056
+ target: '#done',
1057
+ actions: rememberCaptainOutput,
1058
+ },
878
1059
  {
879
1060
  guard: guardIs('hasFindings'),
880
1061
  target: '#respondToReview',
881
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
1062
+ actions: [
1063
+ rememberCaptainOutput,
1064
+ assign({ reviewSubject: () => 'commit' as const }),
1065
+ ],
882
1066
  },
883
1067
  ]),
884
1068
  onError: captainError,
@@ -887,11 +1071,13 @@ export const codingMachine = setup({
887
1071
 
888
1072
  reviewIrTaskCommitCode: {
889
1073
  id: 'reviewIrTaskCommitCode',
890
- description:
891
- 'CODE-9: Reviewer reviews an IR-task commit whose changes are only outside @specs/{user,dev,test}/.',
1074
+ description: stateDescriptions.reviewIrTaskCommitCode,
1075
+ meta: playbookMeta('reviewIrTaskCommitCode'),
1076
+ tags: ['playbook.busy'],
892
1077
  invoke: {
893
- src: 'captain',
894
- input: ({ context }): CaptainInput => ({
1078
+ src: 'player',
1079
+ input: ({ context }): PlayerInput => ({
1080
+ stateId: 'reviewIrTaskCommitCode',
895
1081
  player: 'Reviewer',
896
1082
  sourceItem: 'CODE-9',
897
1083
  ...bossReplyInputFields(context),
@@ -915,13 +1101,28 @@ export const codingMachine = setup({
915
1101
  },
916
1102
  }),
917
1103
  onDone: withNeedsBossReplyTransition('reviewIrTaskCommitCode', [
918
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
919
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
920
- { guard: noFindingsAfter('done'), target: '#done', actions: rememberCaptainOutput },
1104
+ {
1105
+ guard: noFindingsAfter('continueIr'),
1106
+ target: '#continueIr',
1107
+ actions: rememberCaptainOutput,
1108
+ },
1109
+ {
1110
+ guard: noFindingsAfter('summarizeSpecs'),
1111
+ target: '#summarizeSpecs',
1112
+ actions: rememberCaptainOutput,
1113
+ },
1114
+ {
1115
+ guard: noFindingsAfter('done'),
1116
+ target: '#done',
1117
+ actions: rememberCaptainOutput,
1118
+ },
921
1119
  {
922
1120
  guard: guardIs('hasFindings'),
923
1121
  target: '#respondToReview',
924
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
1122
+ actions: [
1123
+ rememberCaptainOutput,
1124
+ assign({ reviewSubject: () => 'commit' as const }),
1125
+ ],
925
1126
  },
926
1127
  ]),
927
1128
  onError: captainError,
@@ -930,11 +1131,13 @@ export const codingMachine = setup({
930
1131
 
931
1132
  reviewIrTaskCommitMixed: {
932
1133
  id: 'reviewIrTaskCommitMixed',
933
- description:
934
- 'CODE-10: Reviewer reviews an IR-task commit whose changes span both @specs/{user,dev,test}/ and other files.',
1134
+ description: stateDescriptions.reviewIrTaskCommitMixed,
1135
+ meta: playbookMeta('reviewIrTaskCommitMixed'),
1136
+ tags: ['playbook.busy'],
935
1137
  invoke: {
936
- src: 'captain',
937
- input: ({ context }): CaptainInput => ({
1138
+ src: 'player',
1139
+ input: ({ context }): PlayerInput => ({
1140
+ stateId: 'reviewIrTaskCommitMixed',
938
1141
  player: 'Reviewer',
939
1142
  sourceItem: 'CODE-10',
940
1143
  ...bossReplyInputFields(context),
@@ -943,6 +1146,8 @@ export const codingMachine = setup({
943
1146
  prompt: [
944
1147
  'Review the latest commit.',
945
1148
  'Refer to the commit message.',
1149
+ 'Flag any issues or improvements (numbered; no duplication).',
1150
+ "Think thoroughly — don't just approve or reject.",
946
1151
  'Verify any affected spec items are:',
947
1152
  '',
948
1153
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -951,8 +1156,6 @@ export const codingMachine = setup({
951
1156
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
952
1157
  '',
953
1158
  'Flag anything missing, redundant, over-specified, or under-specified.',
954
- 'Flag any issues or improvements (numbered; no duplication).',
955
- "Think thoroughly — don't just approve or reject.",
956
1159
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
957
1160
  'Verify @specs/map.md reflects the changes.',
958
1161
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -966,13 +1169,28 @@ export const codingMachine = setup({
966
1169
  },
967
1170
  }),
968
1171
  onDone: withNeedsBossReplyTransition('reviewIrTaskCommitMixed', [
969
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
970
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
971
- { guard: noFindingsAfter('done'), target: '#done', actions: rememberCaptainOutput },
1172
+ {
1173
+ guard: noFindingsAfter('continueIr'),
1174
+ target: '#continueIr',
1175
+ actions: rememberCaptainOutput,
1176
+ },
1177
+ {
1178
+ guard: noFindingsAfter('summarizeSpecs'),
1179
+ target: '#summarizeSpecs',
1180
+ actions: rememberCaptainOutput,
1181
+ },
1182
+ {
1183
+ guard: noFindingsAfter('done'),
1184
+ target: '#done',
1185
+ actions: rememberCaptainOutput,
1186
+ },
972
1187
  {
973
1188
  guard: guardIs('hasFindings'),
974
1189
  target: '#respondToReview',
975
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
1190
+ actions: [
1191
+ rememberCaptainOutput,
1192
+ assign({ reviewSubject: () => 'commit' as const }),
1193
+ ],
976
1194
  },
977
1195
  ]),
978
1196
  onError: captainError,
@@ -981,11 +1199,13 @@ export const codingMachine = setup({
981
1199
 
982
1200
  reviewChangesSpecs: {
983
1201
  id: 'reviewChangesSpecs',
984
- description:
985
- 'CODE-11: Reviewer reviews uncommitted Coder changes that touch only @specs/{user,dev,test}/ with no accompanying rebuttals.',
1202
+ description: stateDescriptions.reviewChangesSpecs,
1203
+ meta: playbookMeta('reviewChangesSpecs'),
1204
+ tags: ['playbook.busy'],
986
1205
  invoke: {
987
- src: 'captain',
988
- input: ({ context }): CaptainInput => ({
1206
+ src: 'player',
1207
+ input: ({ context }): PlayerInput => ({
1208
+ stateId: 'reviewChangesSpecs',
989
1209
  player: 'Reviewer',
990
1210
  sourceItem: 'CODE-11',
991
1211
  ...bossReplyInputFields(context),
@@ -1006,7 +1226,8 @@ export const codingMachine = setup({
1006
1226
  'Do not edit files or commit; report findings only.',
1007
1227
  ].join('\n'),
1008
1228
  result: {
1009
- noFindings: 'The uncommitted spec-only changes are ready to commit.',
1229
+ noFindings:
1230
+ 'The uncommitted spec-only changes are ready to commit.',
1010
1231
  hasFindings:
1011
1232
  'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
1012
1233
  needsBossReply: needsBossReplyDescription,
@@ -1021,7 +1242,10 @@ export const codingMachine = setup({
1021
1242
  {
1022
1243
  guard: guardIs('hasFindings'),
1023
1244
  target: '#respondToReview',
1024
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1245
+ actions: [
1246
+ rememberCaptainOutput,
1247
+ assign({ reviewSubject: () => 'changes' as const }),
1248
+ ],
1025
1249
  },
1026
1250
  ]),
1027
1251
  onError: captainError,
@@ -1030,11 +1254,13 @@ export const codingMachine = setup({
1030
1254
 
1031
1255
  reviewChangesCode: {
1032
1256
  id: 'reviewChangesCode',
1033
- description:
1034
- 'CODE-12: Reviewer reviews uncommitted Coder changes that touch only files outside @specs/{user,dev,test}/ with no accompanying rebuttals.',
1257
+ description: stateDescriptions.reviewChangesCode,
1258
+ meta: playbookMeta('reviewChangesCode'),
1259
+ tags: ['playbook.busy'],
1035
1260
  invoke: {
1036
- src: 'captain',
1037
- input: ({ context }): CaptainInput => ({
1261
+ src: 'player',
1262
+ input: ({ context }): PlayerInput => ({
1263
+ stateId: 'reviewChangesCode',
1038
1264
  player: 'Reviewer',
1039
1265
  sourceItem: 'CODE-12',
1040
1266
  ...bossReplyInputFields(context),
@@ -1049,7 +1275,8 @@ export const codingMachine = setup({
1049
1275
  'Do not edit files or commit; report findings only.',
1050
1276
  ].join('\n'),
1051
1277
  result: {
1052
- noFindings: 'The uncommitted code-only changes are ready to commit.',
1278
+ noFindings:
1279
+ 'The uncommitted code-only changes are ready to commit.',
1053
1280
  hasFindings:
1054
1281
  'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
1055
1282
  needsBossReply: needsBossReplyDescription,
@@ -1064,7 +1291,10 @@ export const codingMachine = setup({
1064
1291
  {
1065
1292
  guard: guardIs('hasFindings'),
1066
1293
  target: '#respondToReview',
1067
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1294
+ actions: [
1295
+ rememberCaptainOutput,
1296
+ assign({ reviewSubject: () => 'changes' as const }),
1297
+ ],
1068
1298
  },
1069
1299
  ]),
1070
1300
  onError: captainError,
@@ -1073,17 +1303,21 @@ export const codingMachine = setup({
1073
1303
 
1074
1304
  reviewChangesMixed: {
1075
1305
  id: 'reviewChangesMixed',
1076
- description:
1077
- 'CODE-13: Reviewer reviews uncommitted Coder changes that touch both @specs/{user,dev,test}/ and other files with no accompanying rebuttals.',
1306
+ description: stateDescriptions.reviewChangesMixed,
1307
+ meta: playbookMeta('reviewChangesMixed'),
1308
+ tags: ['playbook.busy'],
1078
1309
  invoke: {
1079
- src: 'captain',
1080
- input: ({ context }): CaptainInput => ({
1310
+ src: 'player',
1311
+ input: ({ context }): PlayerInput => ({
1312
+ stateId: 'reviewChangesMixed',
1081
1313
  player: 'Reviewer',
1082
1314
  sourceItem: 'CODE-13',
1083
1315
  ...bossReplyInputFields(context),
1084
1316
  prompt: [
1085
1317
  'Review the unstaged and untracked changes in the context of the staged changes.',
1086
1318
  'Understand the intent.',
1319
+ 'Flag any issues or improvements (numbered; no duplication).',
1320
+ "Think thoroughly — don't just approve or reject.",
1087
1321
  'Verify any affected spec items are:',
1088
1322
  '',
1089
1323
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -1092,8 +1326,6 @@ export const codingMachine = setup({
1092
1326
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
1093
1327
  '',
1094
1328
  'Flag anything missing, redundant, over-specified, or under-specified.',
1095
- 'Flag any issues or improvements (numbered; no duplication).',
1096
- "Think thoroughly — don't just approve or reject.",
1097
1329
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
1098
1330
  'Verify @specs/map.md reflects the changes.',
1099
1331
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -1115,7 +1347,10 @@ export const codingMachine = setup({
1115
1347
  {
1116
1348
  guard: guardIs('hasFindings'),
1117
1349
  target: '#respondToReview',
1118
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1350
+ actions: [
1351
+ rememberCaptainOutput,
1352
+ assign({ reviewSubject: () => 'changes' as const }),
1353
+ ],
1119
1354
  },
1120
1355
  ]),
1121
1356
  onError: captainError,
@@ -1124,11 +1359,13 @@ export const codingMachine = setup({
1124
1359
 
1125
1360
  reviewChangesAndChallengesSpecs: {
1126
1361
  id: 'reviewChangesAndChallengesSpecs',
1127
- description:
1128
- 'CODE-15: Reviewer reviews uncommitted Coder changes that touch only @specs/{user,dev,test}/ and adjudicates accompanying rebuttals in one round.',
1362
+ description: stateDescriptions.reviewChangesAndChallengesSpecs,
1363
+ meta: playbookMeta('reviewChangesAndChallengesSpecs'),
1364
+ tags: ['playbook.busy'],
1129
1365
  invoke: {
1130
- src: 'captain',
1131
- input: ({ context }): CaptainInput => ({
1366
+ src: 'player',
1367
+ input: ({ context }): PlayerInput => ({
1368
+ stateId: 'reviewChangesAndChallengesSpecs',
1132
1369
  player: 'Reviewer',
1133
1370
  sourceItem: 'CODE-15',
1134
1371
  ...bossReplyInputFields(context),
@@ -1159,29 +1396,37 @@ export const codingMachine = setup({
1159
1396
  needsBossReply: needsBossReplyDescription,
1160
1397
  },
1161
1398
  }),
1162
- onDone: withNeedsBossReplyTransition('reviewChangesAndChallengesSpecs', [
1163
- {
1164
- guard: guardIs('approved'),
1165
- target: '#commitJoint',
1166
- actions: rememberCaptainOutput,
1167
- },
1168
- {
1169
- guard: guardIs('needsRevision'),
1170
- target: '#respondToReview',
1171
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1172
- },
1173
- ]),
1399
+ onDone: withNeedsBossReplyTransition(
1400
+ 'reviewChangesAndChallengesSpecs',
1401
+ [
1402
+ {
1403
+ guard: guardIs('approved'),
1404
+ target: '#commitJoint',
1405
+ actions: rememberCaptainOutput,
1406
+ },
1407
+ {
1408
+ guard: guardIs('needsRevision'),
1409
+ target: '#respondToReview',
1410
+ actions: [
1411
+ rememberCaptainOutput,
1412
+ assign({ reviewSubject: () => 'changes' as const }),
1413
+ ],
1414
+ },
1415
+ ],
1416
+ ),
1174
1417
  onError: captainError,
1175
1418
  },
1176
1419
  },
1177
1420
 
1178
1421
  reviewChangesAndChallengesCode: {
1179
1422
  id: 'reviewChangesAndChallengesCode',
1180
- description:
1181
- 'CODE-16: Reviewer reviews uncommitted Coder changes that touch only files outside @specs/{user,dev,test}/ and adjudicates accompanying rebuttals in one round.',
1423
+ description: stateDescriptions.reviewChangesAndChallengesCode,
1424
+ meta: playbookMeta('reviewChangesAndChallengesCode'),
1425
+ tags: ['playbook.busy'],
1182
1426
  invoke: {
1183
- src: 'captain',
1184
- input: ({ context }): CaptainInput => ({
1427
+ src: 'player',
1428
+ input: ({ context }): PlayerInput => ({
1429
+ stateId: 'reviewChangesAndChallengesCode',
1185
1430
  player: 'Reviewer',
1186
1431
  sourceItem: 'CODE-16',
1187
1432
  ...bossReplyInputFields(context),
@@ -1215,7 +1460,10 @@ export const codingMachine = setup({
1215
1460
  {
1216
1461
  guard: guardIs('needsRevision'),
1217
1462
  target: '#respondToReview',
1218
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1463
+ actions: [
1464
+ rememberCaptainOutput,
1465
+ assign({ reviewSubject: () => 'changes' as const }),
1466
+ ],
1219
1467
  },
1220
1468
  ]),
1221
1469
  onError: captainError,
@@ -1224,11 +1472,13 @@ export const codingMachine = setup({
1224
1472
 
1225
1473
  reviewChangesAndChallengesMixed: {
1226
1474
  id: 'reviewChangesAndChallengesMixed',
1227
- description:
1228
- 'CODE-17: Reviewer reviews uncommitted Coder changes that touch both @specs/{user,dev,test}/ and other files and adjudicates accompanying rebuttals in one round.',
1475
+ description: stateDescriptions.reviewChangesAndChallengesMixed,
1476
+ meta: playbookMeta('reviewChangesAndChallengesMixed'),
1477
+ tags: ['playbook.busy'],
1229
1478
  invoke: {
1230
- src: 'captain',
1231
- input: ({ context }): CaptainInput => ({
1479
+ src: 'player',
1480
+ input: ({ context }): PlayerInput => ({
1481
+ stateId: 'reviewChangesAndChallengesMixed',
1232
1482
  player: 'Reviewer',
1233
1483
  sourceItem: 'CODE-17',
1234
1484
  ...bossReplyInputFields(context),
@@ -1237,6 +1487,8 @@ export const codingMachine = setup({
1237
1487
  prompt: [
1238
1488
  'Review the unstaged and untracked changes in the context of the staged changes.',
1239
1489
  'Understand the intent.',
1490
+ 'Flag any issues or improvements (numbered; no duplication).',
1491
+ "Think thoroughly — don't just approve or reject.",
1240
1492
  'Verify any affected spec items are:',
1241
1493
  '',
1242
1494
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -1245,8 +1497,6 @@ export const codingMachine = setup({
1245
1497
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
1246
1498
  '',
1247
1499
  'Flag anything missing, redundant, over-specified, or under-specified.',
1248
- 'Flag any issues or improvements (numbered; no duplication).',
1249
- "Think thoroughly — don't just approve or reject.",
1250
1500
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
1251
1501
  'Verify @specs/map.md reflects the changes.',
1252
1502
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -1261,28 +1511,37 @@ export const codingMachine = setup({
1261
1511
  needsBossReply: needsBossReplyDescription,
1262
1512
  },
1263
1513
  }),
1264
- onDone: withNeedsBossReplyTransition('reviewChangesAndChallengesMixed', [
1265
- {
1266
- guard: guardIs('approved'),
1267
- target: '#commitJoint',
1268
- actions: rememberCaptainOutput,
1269
- },
1270
- {
1271
- guard: guardIs('needsRevision'),
1272
- target: '#respondToReview',
1273
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1274
- },
1275
- ]),
1514
+ onDone: withNeedsBossReplyTransition(
1515
+ 'reviewChangesAndChallengesMixed',
1516
+ [
1517
+ {
1518
+ guard: guardIs('approved'),
1519
+ target: '#commitJoint',
1520
+ actions: rememberCaptainOutput,
1521
+ },
1522
+ {
1523
+ guard: guardIs('needsRevision'),
1524
+ target: '#respondToReview',
1525
+ actions: [
1526
+ rememberCaptainOutput,
1527
+ assign({ reviewSubject: () => 'changes' as const }),
1528
+ ],
1529
+ },
1530
+ ],
1531
+ ),
1276
1532
  onError: captainError,
1277
1533
  },
1278
1534
  },
1279
1535
 
1280
1536
  adjudicateChallenges: {
1281
1537
  id: 'adjudicateChallenges',
1282
- description: 'CODE-14: Reviewer adjudicates Coder rebuttals against the prior review when Coder produced no code edits this round.',
1538
+ description: stateDescriptions.adjudicateChallenges,
1539
+ meta: playbookMeta('adjudicateChallenges'),
1540
+ tags: ['playbook.busy'],
1283
1541
  invoke: {
1284
- src: 'captain',
1285
- input: ({ context }): CaptainInput => ({
1542
+ src: 'player',
1543
+ input: ({ context }): PlayerInput => ({
1544
+ stateId: 'adjudicateChallenges',
1286
1545
  player: 'Reviewer',
1287
1546
  sourceItem: 'CODE-14',
1288
1547
  ...bossReplyInputFields(context),
@@ -1349,11 +1608,13 @@ export const codingMachine = setup({
1349
1608
 
1350
1609
  commitCoderInitial: {
1351
1610
  id: 'commitCoderInitial',
1352
- description:
1353
- 'CODE-18: Committer commits Coder Initial Changes when Reviewer has not played since the last commit.',
1611
+ description: stateDescriptions.commitCoderInitial,
1612
+ meta: playbookMeta('commitCoderInitial'),
1613
+ tags: ['playbook.busy'],
1354
1614
  invoke: {
1355
- src: 'captain',
1356
- input: ({ context }): CaptainInput => ({
1615
+ src: 'player',
1616
+ input: ({ context }): PlayerInput => ({
1617
+ stateId: 'commitCoderInitial',
1357
1618
  player: 'Committer',
1358
1619
  sourceItem: 'CODE-18',
1359
1620
  ...bossReplyInputFields(context),
@@ -1361,14 +1622,17 @@ export const codingMachine = setup({
1361
1622
  committerPlayer: context.committerPlayer,
1362
1623
  prompt: [
1363
1624
  'Make a commit of the changes that belong in the repo, following @specs/dev/git.md (reread if necessary).',
1364
- 'Write concisely.',
1625
+ 'Write the commit message concisely.',
1365
1626
  'Coder is <coder-llm>.',
1366
1627
  'Format the `Co-authored-by` `<model>` token as the conventional human form of the substituted id (e.g., `claude-opus-4-7` → `Claude-Opus-4.7`, `gpt-5.5` → `GPT-5.5`).',
1367
1628
  ].join('\n'),
1368
1629
  result: {
1369
- committedSpecs: 'Committed changes that touch only @specs/{user,dev,test}/.',
1370
- committedCode: 'Committed changes that touch only files outside @specs/{user,dev,test}/.',
1371
- committedMixed: 'Committed changes that span both @specs/{user,dev,test}/ and other files.',
1630
+ committedSpecs:
1631
+ 'Committed changes that touch only @specs/{user,dev,test}/.',
1632
+ committedCode:
1633
+ 'Committed changes that touch only files outside @specs/{user,dev,test}/.',
1634
+ committedMixed:
1635
+ 'Committed changes that span both @specs/{user,dev,test}/ and other files.',
1372
1636
  noRelevantChanges: 'There are no relevant changes to commit.',
1373
1637
  needsBossInput: 'Committing requires additional Boss input.',
1374
1638
  needsBossReply: needsBossReplyDescription,
@@ -1405,8 +1669,16 @@ export const codingMachine = setup({
1405
1669
  target: '#reviewIrTaskCommitMixed',
1406
1670
  actions: rememberCaptainOutput,
1407
1671
  },
1408
- { guard: guardIs('noRelevantChanges'), target: '#ready', actions: rememberCaptainOutput },
1409
- { guard: guardIs('needsBossInput'), target: '#ready', actions: rememberCaptainOutput },
1672
+ {
1673
+ guard: guardIs('noRelevantChanges'),
1674
+ target: '#ready',
1675
+ actions: rememberCaptainOutput,
1676
+ },
1677
+ {
1678
+ guard: guardIs('needsBossInput'),
1679
+ target: '#ready',
1680
+ actions: rememberCaptainOutput,
1681
+ },
1410
1682
  ]),
1411
1683
  onError: captainError,
1412
1684
  },
@@ -1414,11 +1686,13 @@ export const codingMachine = setup({
1414
1686
 
1415
1687
  commitJoint: {
1416
1688
  id: 'commitJoint',
1417
- description:
1418
- 'CODE-19: Committer commits changes when both Coder and Reviewer have played since the last commit.',
1689
+ description: stateDescriptions.commitJoint,
1690
+ meta: playbookMeta('commitJoint'),
1691
+ tags: ['playbook.busy'],
1419
1692
  invoke: {
1420
- src: 'captain',
1421
- input: ({ context }): CaptainInput => ({
1693
+ src: 'player',
1694
+ input: ({ context }): PlayerInput => ({
1695
+ stateId: 'commitJoint',
1422
1696
  player: 'Committer',
1423
1697
  sourceItem: 'CODE-19',
1424
1698
  ...bossReplyInputFields(context),
@@ -1427,7 +1701,7 @@ export const codingMachine = setup({
1427
1701
  committerPlayer: context.committerPlayer,
1428
1702
  prompt: [
1429
1703
  'Make a commit of the changes that belong in the repo, following @specs/dev/git.md (reread if necessary).',
1430
- 'Write concisely.',
1704
+ 'Write the commit message concisely.',
1431
1705
  'Coder is <coder-llm>; Reviewer is <reviewer-llm>.',
1432
1706
  'Format the `Co-authored-by` `<model>` token as the conventional human form of the substituted id (e.g., `claude-opus-4-7` → `Claude-Opus-4.7`, `gpt-5.5` → `GPT-5.5`).',
1433
1707
  ].join('\n'),
@@ -1441,24 +1715,35 @@ export const codingMachine = setup({
1441
1715
  onDone: withNeedsBossReplyTransition('commitJoint', [
1442
1716
  {
1443
1717
  guard: ({ context, event }) =>
1444
- outputOf(event)?.guard === 'committed' && context.afterReview === 'continueIr',
1718
+ outputOf(event)?.guard === 'committed' &&
1719
+ context.afterReview === 'continueIr',
1445
1720
  target: '#continueIr',
1446
1721
  actions: rememberCaptainOutput,
1447
1722
  },
1448
1723
  {
1449
1724
  guard: ({ context, event }) =>
1450
- outputOf(event)?.guard === 'committed' && context.afterReview === 'summarizeSpecs',
1725
+ outputOf(event)?.guard === 'committed' &&
1726
+ context.afterReview === 'summarizeSpecs',
1451
1727
  target: '#summarizeSpecs',
1452
1728
  actions: rememberCaptainOutput,
1453
1729
  },
1454
1730
  {
1455
1731
  guard: ({ context, event }) =>
1456
- outputOf(event)?.guard === 'committed' && context.afterReview === 'done',
1732
+ outputOf(event)?.guard === 'committed' &&
1733
+ context.afterReview === 'done',
1734
+ target: '#done',
1735
+ actions: rememberCaptainOutput,
1736
+ },
1737
+ {
1738
+ guard: guardIs('noRelevantChanges'),
1457
1739
  target: '#done',
1458
1740
  actions: rememberCaptainOutput,
1459
1741
  },
1460
- { guard: guardIs('noRelevantChanges'), target: '#done', actions: rememberCaptainOutput },
1461
- { guard: guardIs('needsBossInput'), target: '#ready', actions: rememberCaptainOutput },
1742
+ {
1743
+ guard: guardIs('needsBossInput'),
1744
+ target: '#ready',
1745
+ actions: rememberCaptainOutput,
1746
+ },
1462
1747
  ]),
1463
1748
  onError: captainError,
1464
1749
  },
@@ -1466,14 +1751,16 @@ export const codingMachine = setup({
1466
1751
 
1467
1752
  failed: {
1468
1753
  id: 'failed',
1469
- description:
1470
- 'Captures the last Captain error so the runner can report it. Boss may resume from here.',
1754
+ description: stateDescriptions.failed,
1755
+ meta: playbookMeta('failed'),
1756
+ tags: ['playbook.parked'],
1471
1757
  on: readyEvents,
1472
1758
  },
1473
1759
 
1474
1760
  done: {
1475
1761
  id: 'done',
1476
- description: 'The selected coding workflow has completed.',
1762
+ description: stateDescriptions.done,
1763
+ meta: playbookMeta('done'),
1477
1764
  type: 'final',
1478
1765
  },
1479
1766
  },