@sublang/playbook 0.8.0 → 1.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 (55) hide show
  1. package/README.md +243 -193
  2. package/package.json +52 -17
  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 +580 -0
  12. package/reference/sdlc/code.playbook/bin/run.js +893 -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 +470 -182
  19. package/reference/sdlc/code.playbook/code.gears.md +11 -10
  20. package/reference/sdlc/code.playbook/code.playbook.d.ts +18 -9
  21. package/reference/sdlc/code.playbook/code.playbook.js +1098 -202
  22. package/reference/sdlc/code.playbook/code.playbook.ts +1440 -258
  23. package/reference/sdlc/code.playbook/code.registry.d.ts +17 -5
  24. package/reference/sdlc/code.playbook/code.registry.js +49 -34
  25. package/reference/sdlc/code.playbook/code.registry.ts +75 -41
  26. package/reference/sdlc/code.playbook/playbook-captain.d.ts +16 -8
  27. package/reference/sdlc/code.playbook/playbook-captain.js +1005 -240
  28. package/reference/sdlc/code.playbook/playbook-captain.ts +1310 -301
  29. package/reference/sdlc/code.playbook/playbook.config.template.yaml +68 -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 +1097 -80
  42. package/slc/optimize.md +88 -0
  43. package/slc/text2gears.md +247 -5
  44. package/src/runtime.d.ts +145 -3
  45. package/src/runtime.ts +200 -2
  46. package/src/xstate-runtime.d.ts +94 -0
  47. package/src/xstate-runtime.js +1247 -0
  48. package/src/xstate-runtime.ts +1802 -0
  49. package/reference/sdlc/code.playbook/bin/playbook-code.js +0 -487
  50. package/reference/sdlc/code.playbook/code.tmux-play.d.ts +0 -4
  51. package/reference/sdlc/code.playbook/code.tmux-play.js +0 -11
  52. package/reference/sdlc/code.playbook/code.tmux-play.ts +0 -29
  53. package/reference/sdlc/code.playbook/playbook-code.config.template.yaml +0 -72
  54. package/reference/sdlc/code.playbook/tmux-play.config.yaml +0 -55
  55. package/reference/sdlc/code.playbook/tmux-play.production.config.yaml +0 -38
@@ -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>;
@@ -67,8 +68,9 @@ export type CaptainInput = {
67
68
  coderPlayer?: string;
68
69
  reviewerPlayer?: string;
69
70
  // Routing-only: the configured Committer-alias player id (`coder` /
70
- // `reviewer`) threaded from `captain.options.code.committer`
71
- // (PBRT-8 / PBRT-30). It selects which host pane runs a `Committer`
71
+ // `reviewer`) threaded from
72
+ // `captain.options.playbooks.code.options.committer` (PBRT-8 / PBRT-30).
73
+ // It selects which host pane runs a `Committer`
72
74
  // commit; it is not a prompt-placeholder source, so it never affects
73
75
  // <coder-llm> / <reviewer-llm> substitution or any labelled block,
74
76
  // and `input.player` stays `Committer` (PLAYBOOK-3).
@@ -77,6 +79,10 @@ export type CaptainInput = {
77
79
  bossReply?: string;
78
80
  };
79
81
 
82
+ export type PlayerInput = CaptainInput & {
83
+ player: Player;
84
+ };
85
+
80
86
  export type CaptainOutput = {
81
87
  guard: string;
82
88
  irNumber?: string;
@@ -88,6 +94,8 @@ export type CaptainOutput = {
88
94
  [k: string]: unknown;
89
95
  };
90
96
 
97
+ export type PlayerOutput = CaptainOutput;
98
+
91
99
  export type CodingInput = {
92
100
  intent?: string;
93
101
  irNumber?: string;
@@ -120,7 +128,11 @@ export type CodingEvent =
120
128
  intent?: string;
121
129
  irNumber?: string;
122
130
  }
123
- | { type: 'BOSS_REPLY'; answer: string };
131
+ | {
132
+ type: 'BOSS_REPLY';
133
+ answer: string;
134
+ questionId?: ResumableStateId;
135
+ };
124
136
 
125
137
  const jumpableStateIds = [
126
138
  'ready',
@@ -168,39 +180,113 @@ const resumableStateIds = [
168
180
  'commitJoint',
169
181
  ] as const satisfies readonly ResumableStateId[];
170
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
+
171
240
  const outputOf = (event: unknown): CaptainOutput | undefined =>
172
241
  (event as { output?: CaptainOutput }).output;
173
242
 
174
- const guardIs = (guard: string) =>
175
- ({ event }: { event: unknown }) => outputOf(event)?.guard === guard;
243
+ const guardIs =
244
+ (guard: string) =>
245
+ ({ event }: { event: unknown }) =>
246
+ outputOf(event)?.guard === guard;
176
247
 
177
- const guardAndOrigin = (guard: string, origin: ChangeOrigin) =>
248
+ const guardAndOrigin =
249
+ (guard: string, origin: ChangeOrigin) =>
178
250
  ({ context, event }: { context: CodingContext; event: unknown }) =>
179
251
  outputOf(event)?.guard === guard && context.changeOrigin === origin;
180
252
 
181
- const acceptedAfter = (subject: ReviewSubject) =>
253
+ const acceptedAfter =
254
+ (subject: ReviewSubject) =>
182
255
  ({ context, event }: { context: CodingContext; event: unknown }) =>
183
256
  outputOf(event)?.guard === 'accepted' && context.reviewSubject === subject;
184
257
 
185
- const noFindingsAfter = (afterReview: AfterReview) =>
258
+ const noFindingsAfter =
259
+ (afterReview: AfterReview) =>
186
260
  ({ context, event }: { context: CodingContext; event: unknown }) =>
187
- outputOf(event)?.guard === 'noFindings' && context.afterReview === afterReview;
261
+ outputOf(event)?.guard === 'noFindings' &&
262
+ context.afterReview === afterReview;
188
263
 
189
264
  const rememberCaptainOutput = assign({
190
265
  lastResult: ({ event }: { event: unknown }) => outputOf(event),
191
266
  lastError: () => undefined,
192
267
  irNumber: ({ context, event }: { context: CodingContext; event: unknown }) =>
193
268
  outputOf(event)?.irNumber ?? context.irNumber,
194
- taskDescription: ({ context, event }: { context: CodingContext; event: unknown }) =>
195
- outputOf(event)?.taskDescription ?? context.taskDescription,
269
+ taskDescription: ({
270
+ context,
271
+ event,
272
+ }: {
273
+ context: CodingContext;
274
+ event: unknown;
275
+ }) => outputOf(event)?.taskDescription ?? context.taskDescription,
196
276
  reviews: ({ context, event }: { context: CodingContext; event: unknown }) =>
197
277
  outputOf(event)?.reviews ?? context.reviews,
198
- challenges: ({ context, event }: { context: CodingContext; event: unknown }) =>
199
- outputOf(event)?.challenges ?? context.challenges,
278
+ challenges: ({
279
+ context,
280
+ event,
281
+ }: {
282
+ context: CodingContext;
283
+ event: unknown;
284
+ }) => outputOf(event)?.challenges ?? context.challenges,
200
285
  });
201
286
 
202
287
  const rememberCaptainError = assign({
203
- lastError: ({ event }: { event: unknown }) => (event as { error?: unknown }).error,
288
+ lastError: ({ event }: { event: unknown }) =>
289
+ (event as { error?: unknown }).error,
204
290
  });
205
291
 
206
292
  const rememberEmptyBossReplyError = assign({
@@ -214,10 +300,10 @@ const rememberBossInput = assign({
214
300
  (event as { irNumber?: string }).irNumber ?? context.irNumber,
215
301
  });
216
302
 
217
- type CaptainInputFactory = (context: Partial<CodingContext>) => CaptainInput;
303
+ type PlayerInputFactory = (context: Partial<CodingContext>) => PlayerInput;
218
304
 
219
305
  const needsBossReplyDescription =
220
- "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>`.";
221
307
 
222
308
  const bossReplyInputFields = (context: Partial<CodingContext>) => ({
223
309
  pendingBossQuestion: context.pendingBossQuestion,
@@ -243,9 +329,15 @@ const captainStateMetadata = {
243
329
  reviewChangesSpecs: { sourceItem: 'CODE-11', player: 'Reviewer' },
244
330
  reviewChangesCode: { sourceItem: 'CODE-12', player: 'Reviewer' },
245
331
  reviewChangesMixed: { sourceItem: 'CODE-13', player: 'Reviewer' },
246
- reviewChangesAndChallengesSpecs: { sourceItem: 'CODE-15', player: 'Reviewer' },
332
+ reviewChangesAndChallengesSpecs: {
333
+ sourceItem: 'CODE-15',
334
+ player: 'Reviewer',
335
+ },
247
336
  reviewChangesAndChallengesCode: { sourceItem: 'CODE-16', player: 'Reviewer' },
248
- reviewChangesAndChallengesMixed: { sourceItem: 'CODE-17', player: 'Reviewer' },
337
+ reviewChangesAndChallengesMixed: {
338
+ sourceItem: 'CODE-17',
339
+ player: 'Reviewer',
340
+ },
249
341
  adjudicateChallenges: { sourceItem: 'CODE-14', player: 'Reviewer' },
250
342
  commitCoderInitial: { sourceItem: 'CODE-18', player: 'Committer' },
251
343
  commitJoint: { sourceItem: 'CODE-19', player: 'Committer' },
@@ -254,7 +346,8 @@ const captainStateMetadata = {
254
346
  { sourceItem: string; player: Player }
255
347
  >;
256
348
 
257
- const planAndImplementInput: CaptainInputFactory = (context) => ({
349
+ const planAndImplementInput: PlayerInputFactory = (context) => ({
350
+ stateId: 'planAndImplement',
258
351
  player: 'Coder',
259
352
  sourceItem: 'CODE-1',
260
353
  intent: context.intent,
@@ -274,7 +367,8 @@ const planAndImplementInput: CaptainInputFactory = (context) => ({
274
367
  }),
275
368
  });
276
369
 
277
- const continueIrInput: CaptainInputFactory = (context) => ({
370
+ const continueIrInput: PlayerInputFactory = (context) => ({
371
+ stateId: 'continueIr',
278
372
  player: 'Coder',
279
373
  sourceItem: 'CODE-3',
280
374
  irNumber: context.irNumber,
@@ -292,7 +386,8 @@ const continueIrInput: CaptainInputFactory = (context) => ({
292
386
  }),
293
387
  });
294
388
 
295
- const summarizeSpecsInput: CaptainInputFactory = (context) => ({
389
+ const summarizeSpecsInput: PlayerInputFactory = (context) => ({
390
+ stateId: 'summarizeSpecs',
296
391
  player: 'Coder',
297
392
  sourceItem: 'CODE-4',
298
393
  irNumber: context.irNumber,
@@ -328,6 +423,7 @@ const setPendingBossQuestion = (resumeStateId: ResumableStateId) =>
328
423
  }) => {
329
424
  const input = captainStateMetadata[resumeStateId];
330
425
  return {
426
+ questionId: resumeStateId,
331
427
  resumeStateId,
332
428
  sourceItem: input.sourceItem,
333
429
  player: input.player,
@@ -350,7 +446,9 @@ const clearBossReplyContext = assign({
350
446
  const actionsArray = (actions: unknown) =>
351
447
  actions === undefined ? [] : Array.isArray(actions) ? actions : [actions];
352
448
 
353
- const withClearBossReplyContext = <T extends { actions?: unknown }>(transition: T): T => ({
449
+ const withClearBossReplyContext = <T extends { actions?: unknown }>(
450
+ transition: T,
451
+ ): T => ({
354
452
  ...transition,
355
453
  actions: [clearBossReplyContext, ...actionsArray(transition.actions)],
356
454
  });
@@ -370,9 +468,17 @@ const bossInterrupts = (
370
468
  const resumableStates = (ids: readonly ResumableStateId[]) =>
371
469
  ids.map((id) => ({
372
470
  target: `#${id}` as const,
373
- guard: ({ context, event }: { context: CodingContext; event: CodingEvent }) =>
471
+ guard: ({
472
+ context,
473
+ event,
474
+ }: {
475
+ context: CodingContext;
476
+ event: CodingEvent;
477
+ }) =>
374
478
  event.type === 'BOSS_REPLY' &&
375
- context.pendingBossQuestion?.resumeStateId === id,
479
+ context.pendingBossQuestion?.resumeStateId === id &&
480
+ (event.questionId === undefined ||
481
+ event.questionId === context.pendingBossQuestion.questionId),
376
482
  reenter: true,
377
483
  actions: rememberBossReply,
378
484
  }));
@@ -450,8 +556,8 @@ const awaitBossReplyEvents = {
450
556
  ],
451
557
  };
452
558
 
453
- const captainPlaceholder = fromPromise<CaptainOutput, CaptainInput>(async () => {
454
- 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');
455
561
  });
456
562
 
457
563
  export const codingMachine = setup({
@@ -461,7 +567,7 @@ export const codingMachine = setup({
461
567
  input: CodingInput | undefined;
462
568
  },
463
569
  actors: {
464
- captain: captainPlaceholder,
570
+ player: playerPlaceholder,
465
571
  },
466
572
  }).createMachine({
467
573
  id: 'coding',
@@ -479,23 +585,28 @@ export const codingMachine = setup({
479
585
  states: {
480
586
  ready: {
481
587
  id: 'ready',
482
- 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'],
483
591
  on: readyEvents,
484
592
  },
485
593
 
486
594
  awaitBossReply: {
487
595
  id: 'awaitBossReply',
488
- description: 'Waiting for Boss to answer a player question.',
596
+ description: stateDescriptions.awaitBossReply,
597
+ meta: playbookMeta('awaitBossReply'),
598
+ tags: ['playbook.parked'],
489
599
  on: awaitBossReplyEvents,
490
600
  },
491
601
 
492
602
  planAndImplement: {
493
603
  id: 'planAndImplement',
494
- description:
495
- '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'],
496
607
  invoke: {
497
- src: 'captain',
498
- input: ({ context }): CaptainInput => planAndImplementInput(context),
608
+ src: 'player',
609
+ input: ({ context }): PlayerInput => planAndImplementInput(context),
499
610
  onDone: withNeedsBossReplyTransition('planAndImplement', [
500
611
  {
501
612
  guard: guardIs('singleCommitReady'),
@@ -528,10 +639,13 @@ export const codingMachine = setup({
528
639
 
529
640
  respondToReview: {
530
641
  id: 'respondToReview',
531
- description: 'CODE-2: Coder addresses or challenges Reviewer findings.',
642
+ description: stateDescriptions.respondToReview,
643
+ meta: playbookMeta('respondToReview'),
644
+ tags: ['playbook.busy'],
532
645
  invoke: {
533
- src: 'captain',
534
- input: ({ context }): CaptainInput => ({
646
+ src: 'player',
647
+ input: ({ context }): PlayerInput => ({
648
+ stateId: 'respondToReview',
535
649
  player: 'Coder',
536
650
  sourceItem: 'CODE-2',
537
651
  ...bossReplyInputFields(context),
@@ -632,11 +746,12 @@ export const codingMachine = setup({
632
746
 
633
747
  continueIr: {
634
748
  id: 'continueIr',
635
- description:
636
- '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'],
637
752
  invoke: {
638
- src: 'captain',
639
- input: ({ context }): CaptainInput => continueIrInput(context),
753
+ src: 'player',
754
+ input: ({ context }): PlayerInput => continueIrInput(context),
640
755
  onDone: withNeedsBossReplyTransition('continueIr', [
641
756
  {
642
757
  guard: guardIs('taskReady'),
@@ -668,10 +783,12 @@ export const codingMachine = setup({
668
783
 
669
784
  summarizeSpecs: {
670
785
  id: 'summarizeSpecs',
671
- description: 'CODE-4: Coder summarizes a completed IR into minimal spec items.',
786
+ description: stateDescriptions.summarizeSpecs,
787
+ meta: playbookMeta('summarizeSpecs'),
788
+ tags: ['playbook.busy'],
672
789
  invoke: {
673
- src: 'captain',
674
- input: ({ context }): CaptainInput => summarizeSpecsInput(context),
790
+ src: 'player',
791
+ input: ({ context }): PlayerInput => summarizeSpecsInput(context),
675
792
  onDone: withNeedsBossReplyTransition('summarizeSpecs', [
676
793
  {
677
794
  guard: guardIs('specsReady'),
@@ -697,11 +814,13 @@ export const codingMachine = setup({
697
814
 
698
815
  reviewBossCommitSpecs: {
699
816
  id: 'reviewBossCommitSpecs',
700
- description:
701
- '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'],
702
820
  invoke: {
703
- src: 'captain',
704
- input: ({ context }): CaptainInput => ({
821
+ src: 'player',
822
+ input: ({ context }): PlayerInput => ({
823
+ stateId: 'reviewBossCommitSpecs',
705
824
  player: 'Reviewer',
706
825
  sourceItem: 'CODE-5',
707
826
  ...bossReplyInputFields(context),
@@ -730,13 +849,28 @@ export const codingMachine = setup({
730
849
  },
731
850
  }),
732
851
  onDone: withNeedsBossReplyTransition('reviewBossCommitSpecs', [
733
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
734
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
735
- { 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
+ },
736
867
  {
737
868
  guard: guardIs('hasFindings'),
738
869
  target: '#respondToReview',
739
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
870
+ actions: [
871
+ rememberCaptainOutput,
872
+ assign({ reviewSubject: () => 'commit' as const }),
873
+ ],
740
874
  },
741
875
  ]),
742
876
  onError: captainError,
@@ -745,11 +879,13 @@ export const codingMachine = setup({
745
879
 
746
880
  reviewBossCommitCode: {
747
881
  id: 'reviewBossCommitCode',
748
- description:
749
- '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'],
750
885
  invoke: {
751
- src: 'captain',
752
- input: ({ context }): CaptainInput => ({
886
+ src: 'player',
887
+ input: ({ context }): PlayerInput => ({
888
+ stateId: 'reviewBossCommitCode',
753
889
  player: 'Reviewer',
754
890
  sourceItem: 'CODE-6',
755
891
  ...bossReplyInputFields(context),
@@ -772,13 +908,28 @@ export const codingMachine = setup({
772
908
  },
773
909
  }),
774
910
  onDone: withNeedsBossReplyTransition('reviewBossCommitCode', [
775
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
776
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
777
- { 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
+ },
778
926
  {
779
927
  guard: guardIs('hasFindings'),
780
928
  target: '#respondToReview',
781
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
929
+ actions: [
930
+ rememberCaptainOutput,
931
+ assign({ reviewSubject: () => 'commit' as const }),
932
+ ],
782
933
  },
783
934
  ]),
784
935
  onError: captainError,
@@ -787,11 +938,13 @@ export const codingMachine = setup({
787
938
 
788
939
  reviewBossCommitMixed: {
789
940
  id: 'reviewBossCommitMixed',
790
- description:
791
- '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'],
792
944
  invoke: {
793
- src: 'captain',
794
- input: ({ context }): CaptainInput => ({
945
+ src: 'player',
946
+ input: ({ context }): PlayerInput => ({
947
+ stateId: 'reviewBossCommitMixed',
795
948
  player: 'Reviewer',
796
949
  sourceItem: 'CODE-7',
797
950
  ...bossReplyInputFields(context),
@@ -799,6 +952,8 @@ export const codingMachine = setup({
799
952
  prompt: [
800
953
  'Review the latest commit.',
801
954
  'Refer to the commit message.',
955
+ 'Flag any issues or improvements (numbered; no duplication).',
956
+ "Think thoroughly — don't just approve or reject.",
802
957
  'Verify any affected spec items are:',
803
958
  '',
804
959
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -807,8 +962,6 @@ export const codingMachine = setup({
807
962
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
808
963
  '',
809
964
  'Flag anything missing, redundant, over-specified, or under-specified.',
810
- 'Flag any issues or improvements (numbered; no duplication).',
811
- "Think thoroughly — don't just approve or reject.",
812
965
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
813
966
  'Verify @specs/map.md reflects the changes.',
814
967
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -822,13 +975,28 @@ export const codingMachine = setup({
822
975
  },
823
976
  }),
824
977
  onDone: withNeedsBossReplyTransition('reviewBossCommitMixed', [
825
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
826
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
827
- { 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
+ },
828
993
  {
829
994
  guard: guardIs('hasFindings'),
830
995
  target: '#respondToReview',
831
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
996
+ actions: [
997
+ rememberCaptainOutput,
998
+ assign({ reviewSubject: () => 'commit' as const }),
999
+ ],
832
1000
  },
833
1001
  ]),
834
1002
  onError: captainError,
@@ -837,11 +1005,13 @@ export const codingMachine = setup({
837
1005
 
838
1006
  reviewIrTaskCommitSpecs: {
839
1007
  id: 'reviewIrTaskCommitSpecs',
840
- description:
841
- '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'],
842
1011
  invoke: {
843
- src: 'captain',
844
- input: ({ context }): CaptainInput => ({
1012
+ src: 'player',
1013
+ input: ({ context }): PlayerInput => ({
1014
+ stateId: 'reviewIrTaskCommitSpecs',
845
1015
  player: 'Reviewer',
846
1016
  sourceItem: 'CODE-8',
847
1017
  ...bossReplyInputFields(context),
@@ -871,13 +1041,28 @@ export const codingMachine = setup({
871
1041
  },
872
1042
  }),
873
1043
  onDone: withNeedsBossReplyTransition('reviewIrTaskCommitSpecs', [
874
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
875
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
876
- { 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
+ },
877
1059
  {
878
1060
  guard: guardIs('hasFindings'),
879
1061
  target: '#respondToReview',
880
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
1062
+ actions: [
1063
+ rememberCaptainOutput,
1064
+ assign({ reviewSubject: () => 'commit' as const }),
1065
+ ],
881
1066
  },
882
1067
  ]),
883
1068
  onError: captainError,
@@ -886,11 +1071,13 @@ export const codingMachine = setup({
886
1071
 
887
1072
  reviewIrTaskCommitCode: {
888
1073
  id: 'reviewIrTaskCommitCode',
889
- description:
890
- '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'],
891
1077
  invoke: {
892
- src: 'captain',
893
- input: ({ context }): CaptainInput => ({
1078
+ src: 'player',
1079
+ input: ({ context }): PlayerInput => ({
1080
+ stateId: 'reviewIrTaskCommitCode',
894
1081
  player: 'Reviewer',
895
1082
  sourceItem: 'CODE-9',
896
1083
  ...bossReplyInputFields(context),
@@ -914,13 +1101,28 @@ export const codingMachine = setup({
914
1101
  },
915
1102
  }),
916
1103
  onDone: withNeedsBossReplyTransition('reviewIrTaskCommitCode', [
917
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
918
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
919
- { 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
+ },
920
1119
  {
921
1120
  guard: guardIs('hasFindings'),
922
1121
  target: '#respondToReview',
923
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
1122
+ actions: [
1123
+ rememberCaptainOutput,
1124
+ assign({ reviewSubject: () => 'commit' as const }),
1125
+ ],
924
1126
  },
925
1127
  ]),
926
1128
  onError: captainError,
@@ -929,11 +1131,13 @@ export const codingMachine = setup({
929
1131
 
930
1132
  reviewIrTaskCommitMixed: {
931
1133
  id: 'reviewIrTaskCommitMixed',
932
- description:
933
- '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'],
934
1137
  invoke: {
935
- src: 'captain',
936
- input: ({ context }): CaptainInput => ({
1138
+ src: 'player',
1139
+ input: ({ context }): PlayerInput => ({
1140
+ stateId: 'reviewIrTaskCommitMixed',
937
1141
  player: 'Reviewer',
938
1142
  sourceItem: 'CODE-10',
939
1143
  ...bossReplyInputFields(context),
@@ -942,6 +1146,8 @@ export const codingMachine = setup({
942
1146
  prompt: [
943
1147
  'Review the latest commit.',
944
1148
  'Refer to the commit message.',
1149
+ 'Flag any issues or improvements (numbered; no duplication).',
1150
+ "Think thoroughly — don't just approve or reject.",
945
1151
  'Verify any affected spec items are:',
946
1152
  '',
947
1153
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -950,8 +1156,6 @@ export const codingMachine = setup({
950
1156
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
951
1157
  '',
952
1158
  'Flag anything missing, redundant, over-specified, or under-specified.',
953
- 'Flag any issues or improvements (numbered; no duplication).',
954
- "Think thoroughly — don't just approve or reject.",
955
1159
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
956
1160
  'Verify @specs/map.md reflects the changes.',
957
1161
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -965,13 +1169,28 @@ export const codingMachine = setup({
965
1169
  },
966
1170
  }),
967
1171
  onDone: withNeedsBossReplyTransition('reviewIrTaskCommitMixed', [
968
- { guard: noFindingsAfter('continueIr'), target: '#continueIr', actions: rememberCaptainOutput },
969
- { guard: noFindingsAfter('summarizeSpecs'), target: '#summarizeSpecs', actions: rememberCaptainOutput },
970
- { 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
+ },
971
1187
  {
972
1188
  guard: guardIs('hasFindings'),
973
1189
  target: '#respondToReview',
974
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'commit' as const })],
1190
+ actions: [
1191
+ rememberCaptainOutput,
1192
+ assign({ reviewSubject: () => 'commit' as const }),
1193
+ ],
975
1194
  },
976
1195
  ]),
977
1196
  onError: captainError,
@@ -980,11 +1199,13 @@ export const codingMachine = setup({
980
1199
 
981
1200
  reviewChangesSpecs: {
982
1201
  id: 'reviewChangesSpecs',
983
- description:
984
- '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'],
985
1205
  invoke: {
986
- src: 'captain',
987
- input: ({ context }): CaptainInput => ({
1206
+ src: 'player',
1207
+ input: ({ context }): PlayerInput => ({
1208
+ stateId: 'reviewChangesSpecs',
988
1209
  player: 'Reviewer',
989
1210
  sourceItem: 'CODE-11',
990
1211
  ...bossReplyInputFields(context),
@@ -1005,7 +1226,8 @@ export const codingMachine = setup({
1005
1226
  'Do not edit files or commit; report findings only.',
1006
1227
  ].join('\n'),
1007
1228
  result: {
1008
- noFindings: 'The uncommitted spec-only changes are ready to commit.',
1229
+ noFindings:
1230
+ 'The uncommitted spec-only changes are ready to commit.',
1009
1231
  hasFindings:
1010
1232
  'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
1011
1233
  needsBossReply: needsBossReplyDescription,
@@ -1020,7 +1242,10 @@ export const codingMachine = setup({
1020
1242
  {
1021
1243
  guard: guardIs('hasFindings'),
1022
1244
  target: '#respondToReview',
1023
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1245
+ actions: [
1246
+ rememberCaptainOutput,
1247
+ assign({ reviewSubject: () => 'changes' as const }),
1248
+ ],
1024
1249
  },
1025
1250
  ]),
1026
1251
  onError: captainError,
@@ -1029,11 +1254,13 @@ export const codingMachine = setup({
1029
1254
 
1030
1255
  reviewChangesCode: {
1031
1256
  id: 'reviewChangesCode',
1032
- description:
1033
- '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'],
1034
1260
  invoke: {
1035
- src: 'captain',
1036
- input: ({ context }): CaptainInput => ({
1261
+ src: 'player',
1262
+ input: ({ context }): PlayerInput => ({
1263
+ stateId: 'reviewChangesCode',
1037
1264
  player: 'Reviewer',
1038
1265
  sourceItem: 'CODE-12',
1039
1266
  ...bossReplyInputFields(context),
@@ -1048,7 +1275,8 @@ export const codingMachine = setup({
1048
1275
  'Do not edit files or commit; report findings only.',
1049
1276
  ].join('\n'),
1050
1277
  result: {
1051
- noFindings: 'The uncommitted code-only changes are ready to commit.',
1278
+ noFindings:
1279
+ 'The uncommitted code-only changes are ready to commit.',
1052
1280
  hasFindings:
1053
1281
  'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
1054
1282
  needsBossReply: needsBossReplyDescription,
@@ -1063,7 +1291,10 @@ export const codingMachine = setup({
1063
1291
  {
1064
1292
  guard: guardIs('hasFindings'),
1065
1293
  target: '#respondToReview',
1066
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1294
+ actions: [
1295
+ rememberCaptainOutput,
1296
+ assign({ reviewSubject: () => 'changes' as const }),
1297
+ ],
1067
1298
  },
1068
1299
  ]),
1069
1300
  onError: captainError,
@@ -1072,17 +1303,21 @@ export const codingMachine = setup({
1072
1303
 
1073
1304
  reviewChangesMixed: {
1074
1305
  id: 'reviewChangesMixed',
1075
- description:
1076
- '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'],
1077
1309
  invoke: {
1078
- src: 'captain',
1079
- input: ({ context }): CaptainInput => ({
1310
+ src: 'player',
1311
+ input: ({ context }): PlayerInput => ({
1312
+ stateId: 'reviewChangesMixed',
1080
1313
  player: 'Reviewer',
1081
1314
  sourceItem: 'CODE-13',
1082
1315
  ...bossReplyInputFields(context),
1083
1316
  prompt: [
1084
1317
  'Review the unstaged and untracked changes in the context of the staged changes.',
1085
1318
  'Understand the intent.',
1319
+ 'Flag any issues or improvements (numbered; no duplication).',
1320
+ "Think thoroughly — don't just approve or reject.",
1086
1321
  'Verify any affected spec items are:',
1087
1322
  '',
1088
1323
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -1091,8 +1326,6 @@ export const codingMachine = setup({
1091
1326
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
1092
1327
  '',
1093
1328
  'Flag anything missing, redundant, over-specified, or under-specified.',
1094
- 'Flag any issues or improvements (numbered; no duplication).',
1095
- "Think thoroughly — don't just approve or reject.",
1096
1329
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
1097
1330
  'Verify @specs/map.md reflects the changes.',
1098
1331
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -1114,7 +1347,10 @@ export const codingMachine = setup({
1114
1347
  {
1115
1348
  guard: guardIs('hasFindings'),
1116
1349
  target: '#respondToReview',
1117
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1350
+ actions: [
1351
+ rememberCaptainOutput,
1352
+ assign({ reviewSubject: () => 'changes' as const }),
1353
+ ],
1118
1354
  },
1119
1355
  ]),
1120
1356
  onError: captainError,
@@ -1123,11 +1359,13 @@ export const codingMachine = setup({
1123
1359
 
1124
1360
  reviewChangesAndChallengesSpecs: {
1125
1361
  id: 'reviewChangesAndChallengesSpecs',
1126
- description:
1127
- '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'],
1128
1365
  invoke: {
1129
- src: 'captain',
1130
- input: ({ context }): CaptainInput => ({
1366
+ src: 'player',
1367
+ input: ({ context }): PlayerInput => ({
1368
+ stateId: 'reviewChangesAndChallengesSpecs',
1131
1369
  player: 'Reviewer',
1132
1370
  sourceItem: 'CODE-15',
1133
1371
  ...bossReplyInputFields(context),
@@ -1158,29 +1396,37 @@ export const codingMachine = setup({
1158
1396
  needsBossReply: needsBossReplyDescription,
1159
1397
  },
1160
1398
  }),
1161
- onDone: withNeedsBossReplyTransition('reviewChangesAndChallengesSpecs', [
1162
- {
1163
- guard: guardIs('approved'),
1164
- target: '#commitJoint',
1165
- actions: rememberCaptainOutput,
1166
- },
1167
- {
1168
- guard: guardIs('needsRevision'),
1169
- target: '#respondToReview',
1170
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1171
- },
1172
- ]),
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
+ ),
1173
1417
  onError: captainError,
1174
1418
  },
1175
1419
  },
1176
1420
 
1177
1421
  reviewChangesAndChallengesCode: {
1178
1422
  id: 'reviewChangesAndChallengesCode',
1179
- description:
1180
- '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'],
1181
1426
  invoke: {
1182
- src: 'captain',
1183
- input: ({ context }): CaptainInput => ({
1427
+ src: 'player',
1428
+ input: ({ context }): PlayerInput => ({
1429
+ stateId: 'reviewChangesAndChallengesCode',
1184
1430
  player: 'Reviewer',
1185
1431
  sourceItem: 'CODE-16',
1186
1432
  ...bossReplyInputFields(context),
@@ -1214,7 +1460,10 @@ export const codingMachine = setup({
1214
1460
  {
1215
1461
  guard: guardIs('needsRevision'),
1216
1462
  target: '#respondToReview',
1217
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1463
+ actions: [
1464
+ rememberCaptainOutput,
1465
+ assign({ reviewSubject: () => 'changes' as const }),
1466
+ ],
1218
1467
  },
1219
1468
  ]),
1220
1469
  onError: captainError,
@@ -1223,11 +1472,13 @@ export const codingMachine = setup({
1223
1472
 
1224
1473
  reviewChangesAndChallengesMixed: {
1225
1474
  id: 'reviewChangesAndChallengesMixed',
1226
- description:
1227
- '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'],
1228
1478
  invoke: {
1229
- src: 'captain',
1230
- input: ({ context }): CaptainInput => ({
1479
+ src: 'player',
1480
+ input: ({ context }): PlayerInput => ({
1481
+ stateId: 'reviewChangesAndChallengesMixed',
1231
1482
  player: 'Reviewer',
1232
1483
  sourceItem: 'CODE-17',
1233
1484
  ...bossReplyInputFields(context),
@@ -1236,6 +1487,8 @@ export const codingMachine = setup({
1236
1487
  prompt: [
1237
1488
  'Review the unstaged and untracked changes in the context of the staged changes.',
1238
1489
  'Understand the intent.',
1490
+ 'Flag any issues or improvements (numbered; no duplication).',
1491
+ "Think thoroughly — don't just approve or reject.",
1239
1492
  'Verify any affected spec items are:',
1240
1493
  '',
1241
1494
  '- Complete & coherent: sufficient for you to reimplement code.',
@@ -1244,8 +1497,6 @@ export const codingMachine = setup({
1244
1497
  '- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
1245
1498
  '',
1246
1499
  'Flag anything missing, redundant, over-specified, or under-specified.',
1247
- 'Flag any issues or improvements (numbered; no duplication).',
1248
- "Think thoroughly — don't just approve or reject.",
1249
1500
  'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
1250
1501
  'Verify @specs/map.md reflects the changes.',
1251
1502
  "If the change is ready to commit or push, don't raise nitpicks.",
@@ -1260,28 +1511,37 @@ export const codingMachine = setup({
1260
1511
  needsBossReply: needsBossReplyDescription,
1261
1512
  },
1262
1513
  }),
1263
- onDone: withNeedsBossReplyTransition('reviewChangesAndChallengesMixed', [
1264
- {
1265
- guard: guardIs('approved'),
1266
- target: '#commitJoint',
1267
- actions: rememberCaptainOutput,
1268
- },
1269
- {
1270
- guard: guardIs('needsRevision'),
1271
- target: '#respondToReview',
1272
- actions: [rememberCaptainOutput, assign({ reviewSubject: () => 'changes' as const })],
1273
- },
1274
- ]),
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
+ ),
1275
1532
  onError: captainError,
1276
1533
  },
1277
1534
  },
1278
1535
 
1279
1536
  adjudicateChallenges: {
1280
1537
  id: 'adjudicateChallenges',
1281
- 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'],
1282
1541
  invoke: {
1283
- src: 'captain',
1284
- input: ({ context }): CaptainInput => ({
1542
+ src: 'player',
1543
+ input: ({ context }): PlayerInput => ({
1544
+ stateId: 'adjudicateChallenges',
1285
1545
  player: 'Reviewer',
1286
1546
  sourceItem: 'CODE-14',
1287
1547
  ...bossReplyInputFields(context),
@@ -1348,11 +1608,13 @@ export const codingMachine = setup({
1348
1608
 
1349
1609
  commitCoderInitial: {
1350
1610
  id: 'commitCoderInitial',
1351
- description:
1352
- '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'],
1353
1614
  invoke: {
1354
- src: 'captain',
1355
- input: ({ context }): CaptainInput => ({
1615
+ src: 'player',
1616
+ input: ({ context }): PlayerInput => ({
1617
+ stateId: 'commitCoderInitial',
1356
1618
  player: 'Committer',
1357
1619
  sourceItem: 'CODE-18',
1358
1620
  ...bossReplyInputFields(context),
@@ -1360,14 +1622,17 @@ export const codingMachine = setup({
1360
1622
  committerPlayer: context.committerPlayer,
1361
1623
  prompt: [
1362
1624
  'Make a commit of the changes that belong in the repo, following @specs/dev/git.md (reread if necessary).',
1363
- 'Write concisely.',
1625
+ 'Write the commit message concisely.',
1364
1626
  'Coder is <coder-llm>.',
1365
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`).',
1366
1628
  ].join('\n'),
1367
1629
  result: {
1368
- committedSpecs: 'Committed changes that touch only @specs/{user,dev,test}/.',
1369
- committedCode: 'Committed changes that touch only files outside @specs/{user,dev,test}/.',
1370
- 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.',
1371
1636
  noRelevantChanges: 'There are no relevant changes to commit.',
1372
1637
  needsBossInput: 'Committing requires additional Boss input.',
1373
1638
  needsBossReply: needsBossReplyDescription,
@@ -1404,8 +1669,16 @@ export const codingMachine = setup({
1404
1669
  target: '#reviewIrTaskCommitMixed',
1405
1670
  actions: rememberCaptainOutput,
1406
1671
  },
1407
- { guard: guardIs('noRelevantChanges'), target: '#ready', actions: rememberCaptainOutput },
1408
- { 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
+ },
1409
1682
  ]),
1410
1683
  onError: captainError,
1411
1684
  },
@@ -1413,11 +1686,13 @@ export const codingMachine = setup({
1413
1686
 
1414
1687
  commitJoint: {
1415
1688
  id: 'commitJoint',
1416
- description:
1417
- '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'],
1418
1692
  invoke: {
1419
- src: 'captain',
1420
- input: ({ context }): CaptainInput => ({
1693
+ src: 'player',
1694
+ input: ({ context }): PlayerInput => ({
1695
+ stateId: 'commitJoint',
1421
1696
  player: 'Committer',
1422
1697
  sourceItem: 'CODE-19',
1423
1698
  ...bossReplyInputFields(context),
@@ -1426,7 +1701,7 @@ export const codingMachine = setup({
1426
1701
  committerPlayer: context.committerPlayer,
1427
1702
  prompt: [
1428
1703
  'Make a commit of the changes that belong in the repo, following @specs/dev/git.md (reread if necessary).',
1429
- 'Write concisely.',
1704
+ 'Write the commit message concisely.',
1430
1705
  'Coder is <coder-llm>; Reviewer is <reviewer-llm>.',
1431
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`).',
1432
1707
  ].join('\n'),
@@ -1440,24 +1715,35 @@ export const codingMachine = setup({
1440
1715
  onDone: withNeedsBossReplyTransition('commitJoint', [
1441
1716
  {
1442
1717
  guard: ({ context, event }) =>
1443
- outputOf(event)?.guard === 'committed' && context.afterReview === 'continueIr',
1718
+ outputOf(event)?.guard === 'committed' &&
1719
+ context.afterReview === 'continueIr',
1444
1720
  target: '#continueIr',
1445
1721
  actions: rememberCaptainOutput,
1446
1722
  },
1447
1723
  {
1448
1724
  guard: ({ context, event }) =>
1449
- outputOf(event)?.guard === 'committed' && context.afterReview === 'summarizeSpecs',
1725
+ outputOf(event)?.guard === 'committed' &&
1726
+ context.afterReview === 'summarizeSpecs',
1450
1727
  target: '#summarizeSpecs',
1451
1728
  actions: rememberCaptainOutput,
1452
1729
  },
1453
1730
  {
1454
1731
  guard: ({ context, event }) =>
1455
- 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'),
1456
1739
  target: '#done',
1457
1740
  actions: rememberCaptainOutput,
1458
1741
  },
1459
- { guard: guardIs('noRelevantChanges'), target: '#done', actions: rememberCaptainOutput },
1460
- { guard: guardIs('needsBossInput'), target: '#ready', actions: rememberCaptainOutput },
1742
+ {
1743
+ guard: guardIs('needsBossInput'),
1744
+ target: '#ready',
1745
+ actions: rememberCaptainOutput,
1746
+ },
1461
1747
  ]),
1462
1748
  onError: captainError,
1463
1749
  },
@@ -1465,14 +1751,16 @@ export const codingMachine = setup({
1465
1751
 
1466
1752
  failed: {
1467
1753
  id: 'failed',
1468
- description:
1469
- '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'],
1470
1757
  on: readyEvents,
1471
1758
  },
1472
1759
 
1473
1760
  done: {
1474
1761
  id: 'done',
1475
- description: 'The selected coding workflow has completed.',
1762
+ description: stateDescriptions.done,
1763
+ meta: playbookMeta('done'),
1476
1764
  type: 'final',
1477
1765
  },
1478
1766
  },