@sublang/playbook 7.0.0 → 9.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 (62) hide show
  1. package/README.md +20 -7
  2. package/docs/cli.md +88 -43
  3. package/docs/configuration.md +221 -119
  4. package/docs/embedding.md +78 -27
  5. package/package.json +4 -3
  6. package/reference/sdlc/captain.playbook/captain.playbook.js +16 -5
  7. package/reference/sdlc/captain.playbook/captain.playbook.ts +20 -6
  8. package/reference/sdlc/code.md +1 -1
  9. package/reference/sdlc/code.playbook/bin/interactive-session.js +816 -0
  10. package/reference/sdlc/code.playbook/bin/launch-config.js +1078 -116
  11. package/reference/sdlc/code.playbook/bin/playbook.js +489 -34
  12. package/reference/sdlc/code.playbook/bin/run.js +283 -298
  13. package/reference/sdlc/code.playbook/bin/session-store.js +818 -26
  14. package/reference/sdlc/code.playbook/code.fsm.d.ts +9 -6
  15. package/reference/sdlc/code.playbook/code.fsm.introspect.js +2 -2
  16. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +2 -2
  17. package/reference/sdlc/code.playbook/code.fsm.js +18 -15
  18. package/reference/sdlc/code.playbook/code.fsm.ts +21 -21
  19. package/reference/sdlc/code.playbook/code.gears.md +1 -1
  20. package/reference/sdlc/code.playbook/code.playbook.d.ts +2 -1
  21. package/reference/sdlc/code.playbook/code.playbook.js +25 -15
  22. package/reference/sdlc/code.playbook/code.playbook.ts +34 -17
  23. package/reference/sdlc/code.playbook/code.registry.d.ts +5 -13
  24. package/reference/sdlc/code.playbook/code.registry.js +3 -10
  25. package/reference/sdlc/code.playbook/code.registry.ts +7 -32
  26. package/reference/sdlc/code.playbook/playbook-captain.d.ts +39 -14
  27. package/reference/sdlc/code.playbook/playbook-captain.js +1014 -299
  28. package/reference/sdlc/code.playbook/playbook-captain.ts +1450 -406
  29. package/reference/sdlc/code.playbook/playbook.config.template.yaml +41 -49
  30. package/reference/sdlc/decide.md +4 -4
  31. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +10 -10
  32. package/reference/sdlc/decide.playbook/decide.fsm.js +21 -14
  33. package/reference/sdlc/decide.playbook/decide.fsm.ts +27 -23
  34. package/reference/sdlc/decide.playbook/decide.gears.md +3 -5
  35. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +11 -13
  36. package/reference/sdlc/decide.playbook/decide.playbook.js +465 -246
  37. package/reference/sdlc/decide.playbook/decide.playbook.ts +623 -283
  38. package/reference/sdlc/decide.playbook/decide.registry.d.ts +5 -13
  39. package/reference/sdlc/decide.playbook/decide.registry.js +3 -9
  40. package/reference/sdlc/decide.playbook/decide.registry.ts +7 -31
  41. package/reference/sdlc/review.md +4 -5
  42. package/reference/sdlc/review.playbook/review.fsm.d.ts +9 -11
  43. package/reference/sdlc/review.playbook/review.fsm.js +30 -24
  44. package/reference/sdlc/review.playbook/review.fsm.ts +39 -35
  45. package/reference/sdlc/review.playbook/review.gears.md +6 -5
  46. package/reference/sdlc/review.playbook/review.playbook.d.ts +2 -1
  47. package/reference/sdlc/review.playbook/review.playbook.js +29 -23
  48. package/reference/sdlc/review.playbook/review.playbook.ts +38 -28
  49. package/reference/sdlc/review.playbook/review.registry.d.ts +5 -13
  50. package/reference/sdlc/review.playbook/review.registry.js +3 -16
  51. package/reference/sdlc/review.playbook/review.registry.ts +7 -38
  52. package/slc/gears2fsm.md +45 -24
  53. package/slc/link.md +297 -135
  54. package/slc/text2gears.md +19 -18
  55. package/src/runtime.d.ts +21 -16
  56. package/src/runtime.ts +20 -23
  57. package/src/xstate-playbook-runtime.d.ts +34 -20
  58. package/src/xstate-playbook-runtime.js +973 -400
  59. package/src/xstate-playbook-runtime.ts +1203 -457
  60. package/src/xstate-runtime.d.ts +17 -7
  61. package/src/xstate-runtime.js +198 -81
  62. package/src/xstate-runtime.ts +339 -112
@@ -2,14 +2,15 @@
2
2
  # SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
3
 
4
4
  # Shared `playbook` and `playbook run` launcher config.
5
- # Top-level host fields only — no `config:` wrapper, no top-level `players`.
6
- # The launcher injects captain.from and the namespaced <id>-<role> host
7
- # players, then hosts the same Playbook Captain shell interactively or headlessly.
5
+ # Top-level host fields only — no `config:` wrapper. The launcher injects
6
+ # captain.from, retains only players named by a role binding, and hosts the
7
+ # same Playbook Captain shell interactively or headlessly.
8
8
 
9
- # Every agent — the Captain and each playbook role carries its own
10
- # settings inline: an adapter shorthand (claude, codex) or a block with
11
- # adapter/model/effort/permissions. Retuning one agent never changes
12
- # another.
9
+ # The Captain and each stable session player carries its own defaults inline:
10
+ # an adapter shorthand (claude, codex) or a block with
11
+ # adapter/model/effort/permissions. A role may override only model or effort.
12
+ # Omit an override to inherit the player default; set it to false to select
13
+ # that provider's current default explicitly.
13
14
 
14
15
  # Each adapter needs its vendor SDK installed as its own top-level
15
16
  # install root — they are optional peer dependencies, so you pay only
@@ -23,9 +24,9 @@
23
24
  # so they can inspect and work with repository metadata under the sandbox.
24
25
 
25
26
  # The Captain/Judge agent.
26
- # Captain routing/adjudication calls run tool-free. Claude enforces that at
27
- # the provider level; the codex adapter cannot enforce a tool list, so a
28
- # codex captain degrades to a prompt-level restriction (DR-013 A1).
27
+ # Captain routing/adjudication calls run tool-free. Claude and Gemini enforce
28
+ # that at the provider level; codex, kimi, and opencode cannot enforce a tool
29
+ # list, so those captains degrade to a prompt-level restriction (DR-013 A1).
29
30
  captain:
30
31
  adapter: claude
31
32
  model: claude-opus-4-8
@@ -39,55 +40,46 @@ notifications:
39
40
  player_finished: bell
40
41
  turn_finished: desktop
41
42
 
43
+ # Stable player ids are flat exact strings. Dots are a naming convention, not
44
+ # YAML hierarchy. Naming the same id in several role bindings deliberately
45
+ # shares one provider conversation across those playbooks.
46
+ players:
47
+ dev.coder:
48
+ adapter: claude
49
+ model: claude-opus-4-8[1m]
50
+ effort: xhigh
51
+ permissions:
52
+ mode: auto
53
+
54
+ dev.reviewer:
55
+ adapter: codex
56
+ model: gpt-5.5
57
+ effort: xhigh
58
+ permissions:
59
+ mode: auto
60
+ writablePaths: ['.git']
61
+
42
62
  # Enabled playbooks. Each is loaded from its explicit `from` module; the
43
63
  # `<id>` key must equal that module's manifest id. `from`, `command`, and
44
- # `players` are launcher-owned; every other key is that playbook's option
64
+ # `roles` are launcher-owned; every other key is that playbook's option
45
65
  # slice.
46
66
  #
47
- # Nested calls map roles by exact id. CODE and DECIDE call REVIEW with the
48
- # same `coder` and `reviewer` ids, so those child roles continue the nearest
49
- # ancestor sessions. A standalone REVIEW uses its own bindings below.
67
+ # Every manifest role is bound explicitly. These three playbooks intentionally
68
+ # reuse the same two stable players; change an id to isolate its conversation.
50
69
  playbooks:
51
70
  code:
52
71
  from: "@sublang/playbook/code/registry"
53
- players:
54
- coder:
55
- adapter: claude
56
- model: claude-opus-4-8[1m]
57
- effort: xhigh
58
- permissions:
59
- mode: auto
72
+ roles:
73
+ coder: dev.coder
60
74
 
61
75
  review:
62
76
  from: "@sublang/playbook/review/registry"
63
- players:
64
- coder:
65
- adapter: claude
66
- model: claude-opus-4-8[1m]
67
- effort: xhigh
68
- permissions:
69
- mode: auto
70
- reviewer:
71
- adapter: codex
72
- model: gpt-5.5
73
- effort: xhigh
74
- permissions:
75
- mode: auto
76
- writablePaths: ['.git']
77
+ roles:
78
+ coder: dev.coder
79
+ reviewer: dev.reviewer
77
80
 
78
81
  decide:
79
82
  from: "@sublang/playbook/decide/registry"
80
- players:
81
- coder:
82
- adapter: claude
83
- model: claude-opus-4-8[1m]
84
- effort: xhigh
85
- permissions:
86
- mode: auto
87
- reviewer:
88
- adapter: codex
89
- model: gpt-5.5
90
- effort: xhigh
91
- permissions:
92
- mode: auto
93
- writablePaths: ['.git']
83
+ roles:
84
+ coder: dev.coder
85
+ reviewer: dev.reviewer
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Decide
6
6
 
7
- Players:
7
+ Roles:
8
8
 
9
9
  - Coder
10
10
  - Reviewer
@@ -13,7 +13,7 @@ The caller supplies the topic including any specific context.
13
13
 
14
14
  When the caller gives a topic, Captain shall relay the complete topic in quotes (`>`) to Coder and Reviewer concurrently and independently.
15
15
  Captain shall not wait for either proposal before requesting the other.
16
- Captain shall give each player the following instruction:
16
+ Captain shall give the player bound to each role the following instruction:
17
17
 
18
18
  ```markdown
19
19
  Assess whether the topic is better expressed as a few spec items under @specs/packages/ or requires one or more DRs under @specs/decisions/.
@@ -23,7 +23,7 @@ Consult @specs/map.md for relevant context and @specs/meta.md for spec requireme
23
23
  Do not change any files.
24
24
  ```
25
25
 
26
- Neither player shall receive the other's proposal until both proposals are complete and Coder has committed Coder's own proposal.
26
+ Neither role's player shall receive the other role's proposal until both proposals are complete and Coder has committed Coder's own proposal.
27
27
  A Boss interrupt during the parallel proposal pair shall restart the whole pair so both players receive the same new topic and remain independent.
28
28
 
29
29
  When both proposals are complete, Captain shall then give Coder the following instruction:
@@ -36,7 +36,7 @@ Do not change code or implement the proposal.
36
36
 
37
37
  Commit the result as one new commit, following @specs/packages/git.md.
38
38
  Make the commit message explain concisely what changed and why.
39
- Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.
39
+ Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.
40
40
  Coder is <coder-llm>; format the model token in conventional human form.
41
41
  ```
42
42
 
@@ -1,14 +1,18 @@
1
1
  export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
2
2
  readonly [key: string]: JsonValue;
3
3
  };
4
- type Player = 'Coder' | 'Reviewer';
4
+ type Role = 'coder' | 'reviewer';
5
+ export declare const concurrentRoleSets: readonly [readonly ["coder", "reviewer"]];
5
6
  type JumpableStateId = 'independentProposals';
6
7
  type ResumableStateId = 'askCoderProposal' | 'askReviewerProposal' | 'commitCoderProposal';
7
8
  export interface PendingBossQuestion {
8
9
  questionId: ResumableStateId;
9
10
  resumeStateId: ResumableStateId;
10
11
  sourceItem: string;
11
- player: Player;
12
+ asker: {
13
+ kind: 'role';
14
+ roleId: Role;
15
+ };
12
16
  question: string;
13
17
  }
14
18
  type PendingBossQuestions = Partial<Record<ResumableStateId, PendingBossQuestion>>;
@@ -37,7 +41,6 @@ interface ChildFailure {
37
41
  };
38
42
  }
39
43
  export interface DecideContext {
40
- coderLlm: string;
41
44
  callerTopic?: string;
42
45
  coderProposal?: string;
43
46
  reviewerProposal?: string;
@@ -63,17 +66,14 @@ export type DecideEvent = {
63
66
  questionId?: ResumableStateId;
64
67
  answer: string;
65
68
  };
66
- export interface DecideInput {
67
- coderLlm: string;
68
- }
69
+ export type DecideInput = Readonly<Record<string, never>>;
69
70
  export interface PlayerInput {
70
71
  stateId: ResumableStateId;
71
72
  sourceItem: string;
72
73
  prompt: string;
73
74
  result: Record<string, string>;
74
- player: Player;
75
+ role: Role;
75
76
  callerTopic?: string;
76
- coderLlm?: string;
77
77
  pendingBossQuestion?: PendingBossQuestion;
78
78
  bossReply?: string;
79
79
  }
@@ -200,12 +200,12 @@ export declare const decideMachine: import("xstate").StateMachine<DecideContext,
200
200
  } | {
201
201
  type: "validReviewSuccess";
202
202
  params: unknown;
203
- }, never, "done" | "failed" | "awaitBossReply" | "ready" | "commitCoderProposal" | "reviewCommit" | "reportedReviewFailure" | {
203
+ }, never, "done" | "failed" | "awaitBossReply" | "ready" | "reportedReviewFailure" | "commitCoderProposal" | "reviewCommit" | {
204
204
  independentProposals: {
205
205
  coder: "complete" | "working" | "waiting";
206
206
  reviewer: "complete" | "working" | "waiting";
207
207
  };
208
- }, string, DecideInput, ReviewSuccessOutput | DecideFailureOutput, import("xstate").EventObject, import("xstate").MetaObject, {
208
+ }, string, Readonly<Record<string, never>>, ReviewSuccessOutput | DecideFailureOutput, import("xstate").EventObject, import("xstate").MetaObject, {
209
209
  id: "decide";
210
210
  states: {
211
211
  readonly ready: {
@@ -4,6 +4,7 @@
4
4
  // This module defines only the machine, actor contracts, and typed inputs.
5
5
  // The linked runtime supplies the player and nested-playbook actors.
6
6
  import { assign, fromPromise, setup } from 'xstate';
7
+ export const concurrentRoleSets = [['coder', 'reviewer']];
7
8
  const NEEDS_BOSS_REPLY_DESCRIPTION = "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>`.";
8
9
  const INDEPENDENT_PROPOSAL_PROMPT = [
9
10
  '> <caller-topic>',
@@ -22,7 +23,7 @@ const COMMIT_CODER_PROMPT = [
22
23
  '',
23
24
  'Commit the result as one new commit, following @specs/packages/git.md.',
24
25
  'Make the commit message explain concisely what changed and why.',
25
- 'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
26
+ 'Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.',
26
27
  'Coder is <coder-llm>; format the model token in conventional human form.',
27
28
  ].join('\n');
28
29
  const REVIEW_INPUT_TEMPLATE = [
@@ -275,9 +276,18 @@ function resumableStates(ids) {
275
276
  }));
276
277
  }
277
278
  const stateMetadata = {
278
- askCoderProposal: { sourceItem: 'DECIDE-1', player: 'Coder' },
279
- askReviewerProposal: { sourceItem: 'DECIDE-2', player: 'Reviewer' },
280
- commitCoderProposal: { sourceItem: 'DECIDE-3', player: 'Coder' },
279
+ askCoderProposal: {
280
+ sourceItem: 'DECIDE-1',
281
+ asker: { kind: 'role', roleId: 'coder' },
282
+ },
283
+ askReviewerProposal: {
284
+ sourceItem: 'DECIDE-2',
285
+ asker: { kind: 'role', roleId: 'reviewer' },
286
+ },
287
+ commitCoderProposal: {
288
+ sourceItem: 'DECIDE-3',
289
+ asker: { kind: 'role', roleId: 'coder' },
290
+ },
281
291
  };
282
292
  export const decideMachine = setup({
283
293
  types: {
@@ -424,9 +434,7 @@ export const decideMachine = setup({
424
434
  }).createMachine({
425
435
  id: 'decide',
426
436
  initial: 'ready',
427
- context: ({ input }) => ({
428
- coderLlm: input.coderLlm,
429
- }),
437
+ context: () => ({}),
430
438
  output: ({ context }) => {
431
439
  if (context.reviewResult)
432
440
  return context.reviewResult;
@@ -486,7 +494,7 @@ export const decideMachine = setup({
486
494
  playbook: {
487
495
  stateId: 'askCoderProposal',
488
496
  description: 'Coder independently proposes a spec design.',
489
- player: 'Coder',
497
+ role: 'coder',
490
498
  },
491
499
  },
492
500
  invoke: {
@@ -494,7 +502,7 @@ export const decideMachine = setup({
494
502
  input: ({ context }) => ({
495
503
  stateId: 'askCoderProposal',
496
504
  sourceItem: 'DECIDE-1',
497
- player: 'Coder',
505
+ role: 'coder',
498
506
  prompt: INDEPENDENT_PROPOSAL_PROMPT,
499
507
  result: withNeedsBossReply({
500
508
  proposed: 'Coder completed an independent proposal. Output shall include `coderProposal: <verbatim final text>`.',
@@ -602,7 +610,7 @@ export const decideMachine = setup({
602
610
  playbook: {
603
611
  stateId: 'askReviewerProposal',
604
612
  description: 'Reviewer independently proposes a spec design.',
605
- player: 'Reviewer',
613
+ role: 'reviewer',
606
614
  },
607
615
  },
608
616
  invoke: {
@@ -610,7 +618,7 @@ export const decideMachine = setup({
610
618
  input: ({ context }) => ({
611
619
  stateId: 'askReviewerProposal',
612
620
  sourceItem: 'DECIDE-2',
613
- player: 'Reviewer',
621
+ role: 'reviewer',
614
622
  prompt: INDEPENDENT_PROPOSAL_PROMPT,
615
623
  result: withNeedsBossReply({
616
624
  proposed: 'Reviewer completed an independent proposal. Output shall include `reviewerProposal: <verbatim final text>`.',
@@ -721,7 +729,7 @@ export const decideMachine = setup({
721
729
  playbook: {
722
730
  stateId: 'commitCoderProposal',
723
731
  description: 'Coder writes and commits Coder’s independent proposal.',
724
- player: 'Coder',
732
+ role: 'coder',
725
733
  },
726
734
  },
727
735
  invoke: {
@@ -729,12 +737,11 @@ export const decideMachine = setup({
729
737
  input: ({ context }) => ({
730
738
  stateId: 'commitCoderProposal',
731
739
  sourceItem: 'DECIDE-3',
732
- player: 'Coder',
740
+ role: 'coder',
733
741
  prompt: COMMIT_CODER_PROMPT,
734
742
  result: withNeedsBossReply({
735
743
  committed: "Coder committed Coder's proposal. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
736
744
  }),
737
- coderLlm: context.coderLlm,
738
745
  ...bossReplyFields(context, 'commitCoderProposal'),
739
746
  }),
740
747
  onDone: [
@@ -15,7 +15,9 @@ export type JsonValue =
15
15
  | readonly JsonValue[]
16
16
  | { readonly [key: string]: JsonValue };
17
17
 
18
- type Player = 'Coder' | 'Reviewer';
18
+ type Role = 'coder' | 'reviewer';
19
+
20
+ export const concurrentRoleSets = [['coder', 'reviewer']] as const;
19
21
 
20
22
  type JumpableStateId = 'independentProposals';
21
23
 
@@ -28,7 +30,7 @@ export interface PendingBossQuestion {
28
30
  questionId: ResumableStateId;
29
31
  resumeStateId: ResumableStateId;
30
32
  sourceItem: string;
31
- player: Player;
33
+ asker: { kind: 'role'; roleId: Role };
32
34
  question: string;
33
35
  }
34
36
 
@@ -67,7 +69,6 @@ interface ChildFailure {
67
69
  }
68
70
 
69
71
  export interface DecideContext {
70
- coderLlm: string;
71
72
  callerTopic?: string;
72
73
  coderProposal?: string;
73
74
  reviewerProposal?: string;
@@ -95,18 +96,15 @@ export type DecideEvent =
95
96
  answer: string;
96
97
  };
97
98
 
98
- export interface DecideInput {
99
- coderLlm: string;
100
- }
99
+ export type DecideInput = Readonly<Record<string, never>>;
101
100
 
102
101
  export interface PlayerInput {
103
102
  stateId: ResumableStateId;
104
103
  sourceItem: string;
105
104
  prompt: string;
106
105
  result: Record<string, string>;
107
- player: Player;
106
+ role: Role;
108
107
  callerTopic?: string;
109
- coderLlm?: string;
110
108
  pendingBossQuestion?: PendingBossQuestion;
111
109
  bossReply?: string;
112
110
  }
@@ -159,7 +157,7 @@ const COMMIT_CODER_PROMPT = [
159
157
  '',
160
158
  'Commit the result as one new commit, following @specs/packages/git.md.',
161
159
  'Make the commit message explain concisely what changed and why.',
162
- 'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
160
+ 'Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.',
163
161
  'Coder is <coder-llm>; format the model token in conventional human form.',
164
162
  ].join('\n');
165
163
 
@@ -503,11 +501,20 @@ function resumableStates(ids: readonly ResumableStateId[]): any[] {
503
501
 
504
502
  const stateMetadata: Record<
505
503
  ResumableStateId,
506
- { sourceItem: string; player: Player }
504
+ { sourceItem: string; asker: { kind: 'role'; roleId: Role } }
507
505
  > = {
508
- askCoderProposal: { sourceItem: 'DECIDE-1', player: 'Coder' },
509
- askReviewerProposal: { sourceItem: 'DECIDE-2', player: 'Reviewer' },
510
- commitCoderProposal: { sourceItem: 'DECIDE-3', player: 'Coder' },
506
+ askCoderProposal: {
507
+ sourceItem: 'DECIDE-1',
508
+ asker: { kind: 'role', roleId: 'coder' },
509
+ },
510
+ askReviewerProposal: {
511
+ sourceItem: 'DECIDE-2',
512
+ asker: { kind: 'role', roleId: 'reviewer' },
513
+ },
514
+ commitCoderProposal: {
515
+ sourceItem: 'DECIDE-3',
516
+ asker: { kind: 'role', roleId: 'coder' },
517
+ },
511
518
  };
512
519
 
513
520
  export const decideMachine = setup({
@@ -671,9 +678,7 @@ export const decideMachine = setup({
671
678
  }).createMachine({
672
679
  id: 'decide',
673
680
  initial: 'ready',
674
- context: ({ input }): DecideContext => ({
675
- coderLlm: input.coderLlm,
676
- }),
681
+ context: (): DecideContext => ({}),
677
682
  output: ({ context }): DecideOutput => {
678
683
  if (context.reviewResult) return context.reviewResult;
679
684
  if (!context.latestCommit || !context.reviewFailure) {
@@ -732,7 +737,7 @@ export const decideMachine = setup({
732
737
  playbook: {
733
738
  stateId: 'askCoderProposal',
734
739
  description: 'Coder independently proposes a spec design.',
735
- player: 'Coder',
740
+ role: 'coder',
736
741
  },
737
742
  },
738
743
  invoke: {
@@ -740,7 +745,7 @@ export const decideMachine = setup({
740
745
  input: ({ context }): PlayerInput => ({
741
746
  stateId: 'askCoderProposal',
742
747
  sourceItem: 'DECIDE-1',
743
- player: 'Coder',
748
+ role: 'coder',
744
749
  prompt: INDEPENDENT_PROPOSAL_PROMPT,
745
750
  result: withNeedsBossReply({
746
751
  proposed:
@@ -851,7 +856,7 @@ export const decideMachine = setup({
851
856
  stateId: 'askReviewerProposal',
852
857
  description:
853
858
  'Reviewer independently proposes a spec design.',
854
- player: 'Reviewer',
859
+ role: 'reviewer',
855
860
  },
856
861
  },
857
862
  invoke: {
@@ -859,7 +864,7 @@ export const decideMachine = setup({
859
864
  input: ({ context }): PlayerInput => ({
860
865
  stateId: 'askReviewerProposal',
861
866
  sourceItem: 'DECIDE-2',
862
- player: 'Reviewer',
867
+ role: 'reviewer',
863
868
  prompt: INDEPENDENT_PROPOSAL_PROMPT,
864
869
  result: withNeedsBossReply({
865
870
  proposed:
@@ -974,7 +979,7 @@ export const decideMachine = setup({
974
979
  playbook: {
975
980
  stateId: 'commitCoderProposal',
976
981
  description: 'Coder writes and commits Coder’s independent proposal.',
977
- player: 'Coder',
982
+ role: 'coder',
978
983
  },
979
984
  },
980
985
  invoke: {
@@ -982,13 +987,12 @@ export const decideMachine = setup({
982
987
  input: ({ context }): PlayerInput => ({
983
988
  stateId: 'commitCoderProposal',
984
989
  sourceItem: 'DECIDE-3',
985
- player: 'Coder',
990
+ role: 'coder',
986
991
  prompt: COMMIT_CODER_PROMPT,
987
992
  result: withNeedsBossReply({
988
993
  committed:
989
994
  "Coder committed Coder's proposal. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
990
995
  }),
991
- coderLlm: context.coderLlm,
992
996
  ...bossReplyFields(context, 'commitCoderProposal'),
993
997
  }),
994
998
  onDone: [
@@ -3,10 +3,8 @@
3
3
 
4
4
  # Decide
5
5
 
6
- ## Players
6
+ ## Roles
7
7
 
8
- - Boss: the human user (default)
9
- - Captain: the coordinating agent (default)
10
8
  - Coder
11
9
  - Reviewer
12
10
 
@@ -14,7 +12,7 @@
14
12
 
15
13
  The caller supplies a topic as `callerTopic`.
16
14
  Coder and Reviewer receive the complete topic concurrently and independently.
17
- Neither player receives the other player's proposal before both proposals finish and Coder commits Coder's own proposal.
15
+ Neither role's player receives the other role's proposal before both proposals finish and Coder commits Coder's own proposal.
18
16
  A Boss interrupt during the parallel proposal pair restarts the complete pair with the new `callerTopic`.
19
17
 
20
18
  ### DECIDE-1
@@ -64,7 +62,7 @@ When both independent proposals are complete, Captain shall prompt Coder:
64
62
  >
65
63
  > Commit the result as one new commit, following @specs/packages/git.md.
66
64
  > Make the commit message explain concisely what changed and why.
67
- > Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.
65
+ > Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.
68
66
  > Coder is <coder-llm>; format the model token in conventional human form.
69
67
 
70
68
  Results:
@@ -1,12 +1,10 @@
1
1
  import { type PlayerInput, type PlayerOutput, type DecideEvent, type DecideInput, type PendingBossQuestion } from './decide.fsm.js';
2
2
  import type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlayerResult, PlayerSessionStore, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlAction, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType } from '@sublang/playbook/runtime';
3
3
  export type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlayerResult, PlayerSessionStore, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlAction, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, };
4
- type PlayerName = 'Coder' | 'Reviewer';
5
- export interface PlaybookRuntimeOptions extends DecideInput {
6
- playerBinding?: Partial<Record<PlayerName, string>>;
7
- }
8
- declare function composePlayerPrompt(input: PlayerInput): string;
9
- declare function resolvePlayerId(input: PlayerInput, binding: Record<PlayerName, string>): string;
4
+ type RoleId = 'coder' | 'reviewer';
5
+ export type PlaybookRuntimeOptions = DecideInput;
6
+ type PromptIdentity = (roleId: RoleId) => string;
7
+ declare function composePlayerPrompt(input: PlayerInput, promptIdentity: PromptIdentity): string;
10
8
  declare function requiredFieldsFor(description: string): string[];
11
9
  declare function extractJson(raw: string): Record<string, unknown> | null;
12
10
  declare function buildClassifierPrompt(text: string, ctx: {
@@ -29,10 +27,10 @@ declare function normalizeErrorFull(err: unknown): {
29
27
  stack?: string;
30
28
  } | undefined;
31
29
  declare function pendingQuestionsFromContext(context: Record<string, unknown>): PendingBossQuestion[];
30
+ declare function pendingQuestionsForState(state: PlaybookState, context: Record<string, unknown>): PendingBossQuestion[];
32
31
  export declare const createPlaybookRuntime: PlaybookRuntimeFactory<PlaybookRuntimeOptions>;
33
32
  export declare const _internal: {
34
33
  composePlayerPrompt: typeof composePlayerPrompt;
35
- resolvePlayerId: typeof resolvePlayerId;
36
34
  requiredFieldsFor: typeof requiredFieldsFor;
37
35
  extractJson: typeof extractJson;
38
36
  buildClassifierPrompt: typeof buildClassifierPrompt;
@@ -41,24 +39,24 @@ export declare const _internal: {
41
39
  parseAdjudication: typeof parseAdjudication;
42
40
  combineSignals: typeof combineSignals;
43
41
  pendingQuestionsFromContext: typeof pendingQuestionsFromContext;
42
+ pendingQuestionsForState: typeof pendingQuestionsForState;
44
43
  normalizeErrorCompact: typeof normalizeErrorCompact;
45
44
  normalizeErrorFull: typeof normalizeErrorFull;
46
- DEFAULT_PLAYER_BINDING: Readonly<Record<PlayerName, string>>;
47
45
  STATE_DESCRIPTIONS: Readonly<Record<string, string>>;
48
- PLAYER_STATES: readonly [{
46
+ ROLE_STATES: readonly [{
49
47
  readonly stateId: "askCoderProposal";
50
- readonly player: "Coder";
48
+ readonly role: "coder";
51
49
  readonly sourceItem: "DECIDE-1";
52
50
  }, {
53
51
  readonly stateId: "askReviewerProposal";
54
- readonly player: "Reviewer";
52
+ readonly role: "reviewer";
55
53
  readonly sourceItem: "DECIDE-2";
56
54
  }, {
57
55
  readonly stateId: "commitCoderProposal";
58
- readonly player: "Coder";
56
+ readonly role: "coder";
59
57
  readonly sourceItem: "DECIDE-3";
60
58
  }];
61
- PLAYER_STATE_IDS: ReadonlySet<string>;
59
+ ROLE_STATE_IDS: ReadonlySet<string>;
62
60
  VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
63
61
  BOSS_INTERRUPT_TARGETS: readonly ["independentProposals"];
64
62
  CONTINUATION_PREAMBLE: string;