@sublang/playbook 7.0.0 → 8.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 (61) hide show
  1. package/README.md +17 -4
  2. package/docs/cli.md +74 -29
  3. package/docs/configuration.md +209 -112
  4. package/docs/embedding.md +71 -25
  5. package/package.json +4 -3
  6. package/reference/sdlc/captain.playbook/captain.playbook.js +3 -3
  7. package/reference/sdlc/captain.playbook/captain.playbook.ts +3 -3
  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 +5 -5
  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 +7 -11
  18. package/reference/sdlc/code.playbook/code.fsm.ts +9 -17
  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 +12 -13
  22. package/reference/sdlc/code.playbook/code.playbook.ts +22 -15
  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 +970 -289
  28. package/reference/sdlc/code.playbook/playbook-captain.ts +1403 -396
  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 +9 -9
  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 +9 -13
  36. package/reference/sdlc/decide.playbook/decide.playbook.js +171 -134
  37. package/reference/sdlc/decide.playbook/decide.playbook.ts +238 -162
  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 +16 -21
  48. package/reference/sdlc/review.playbook/review.playbook.ts +26 -26
  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 +27 -23
  53. package/slc/link.md +113 -93
  54. package/slc/text2gears.md +19 -18
  55. package/src/runtime.d.ts +20 -16
  56. package/src/runtime.ts +19 -23
  57. package/src/xstate-playbook-runtime.d.ts +21 -17
  58. package/src/xstate-playbook-runtime.js +241 -149
  59. package/src/xstate-playbook-runtime.ts +331 -178
  60. package/src/xstate-runtime.js +63 -24
  61. package/src/xstate-runtime.ts +96 -28
@@ -1,9 +1,10 @@
1
+ import { type XStatePromptIdentity } from '@sublang/playbook/xstate-runtime';
1
2
  import { type PlayerInput, type ReviewInput } from './review.fsm.js';
2
3
  import type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, PlayerResult, PlayerSessionStore } from '@sublang/playbook/runtime';
3
4
  export type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, PlayerResult, PlayerSessionStore, };
4
5
  export type ReviewPlaybookOptions = ReviewInput;
5
6
  /** Keep every line of a relayed runtime value inside its authored quote. */
6
- declare function composePlayerPrompt(input: PlayerInput): string;
7
+ declare function composePlayerPrompt(input: PlayerInput, promptIdentity: XStatePromptIdentity): string;
7
8
  export declare const _internal: {
8
9
  composePlayerPrompt: typeof composePlayerPrompt;
9
10
  VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
@@ -3,15 +3,14 @@
3
3
  //
4
4
  // Generated by slc/link.md (FSM-to-Runtime linker).
5
5
  // Source FSM: ./review.fsm.ts
6
- // Player bind: Coder→coder, Reviewer→reviewer (default lowercased binding)
6
+ // Role metadata: Coder→coder, Reviewer→reviewer (canonical local roles)
7
7
  // Boss event: deterministic START_REVIEW entry; exact Boss text becomes
8
8
  // callerInput; pending player questions retain BOSS_REPLY
9
9
  // Adjudication: LLM judge per state; coderOutput and reviewerOutput are
10
10
  // carried verbatim
11
- // Compat: artifact schema 1 / runtime ABI 1
11
+ // Compat: artifact schema 2 / runtime ABI 1
12
12
  import { RUNTIME_ABI, createXStatePlaybookRuntime, snapshotJsonValue, } from '@sublang/playbook/xstate-runtime';
13
13
  import { reviewMachine, } from './review.fsm.js';
14
- const OPTION_KEYS = new Set(['coderLlm', 'reviewerLlm']);
15
14
  const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
16
15
  const CONTINUATION_PREAMBLE = 'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
17
16
  const VERBATIM_PAYLOAD_FIELDS = new Set([
@@ -27,15 +26,7 @@ function snapshotReviewOptions(value) {
27
26
  }
28
27
  const record = captured;
29
28
  for (const key of Object.keys(record)) {
30
- if (!OPTION_KEYS.has(key)) {
31
- throw new TypeError(`REVIEW runtime options.${key} is not declared`);
32
- }
33
- }
34
- for (const key of OPTION_KEYS) {
35
- const option = record[key];
36
- if (typeof option !== 'string' || option.trim().length === 0) {
37
- throw new TypeError(`REVIEW runtime options.${key} must be a non-empty string`);
38
- }
29
+ throw new TypeError(`REVIEW runtime options.${key} is not declared`);
39
30
  }
40
31
  return captured;
41
32
  }
@@ -48,10 +39,14 @@ function quotedContinuation(value) {
48
39
  return value.replaceAll('\n', '\n> ');
49
40
  }
50
41
  /** Keep every line of a relayed runtime value inside its authored quote. */
51
- function composePlayerPrompt(input) {
42
+ function composePlayerPrompt(input, promptIdentity) {
52
43
  const fields = input;
53
44
  const body = input.prompt.replace(PLACEHOLDER, (match, token, offset, source) => {
54
- const value = fields[placeholderField(token)];
45
+ const value = token === 'coder-llm'
46
+ ? promptIdentity('coder')
47
+ : token === 'reviewer-llm'
48
+ ? promptIdentity('reviewer')
49
+ : fields[placeholderField(token)];
55
50
  if (typeof value !== 'string')
56
51
  return match;
57
52
  const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
@@ -76,28 +71,28 @@ export const _internal = {
76
71
  };
77
72
  const runtimeSpec = {
78
73
  label: 'REVIEW',
79
- compat: { artifactSchema: 1, runtimeAbi: RUNTIME_ABI },
74
+ compat: { artifactSchema: 2, runtimeAbi: RUNTIME_ABI },
80
75
  snapshotOptions: snapshotReviewOptions,
81
76
  entryEvent: { type: 'START_REVIEW', textField: 'callerInput' },
82
- playerStates: {
77
+ roleStates: {
83
78
  reviewInitial: {
84
- player: 'Reviewer',
79
+ role: 'reviewer',
85
80
  label: 'REVIEW-1: Reviewer examines the latest commit against the caller input.',
86
81
  },
87
82
  addressFindings: {
88
- player: 'Coder',
83
+ role: 'coder',
89
84
  label: 'REVIEW-2: Coder accepts or rejects every current review finding.',
90
85
  },
91
86
  reviewAfterCommit: {
92
- player: 'Reviewer',
87
+ role: 'reviewer',
93
88
  label: 'REVIEW-3: Reviewer examines the new review-fix commit and repository state.',
94
89
  },
95
90
  reviewAfterRebuttal: {
96
- player: 'Reviewer',
91
+ role: 'reviewer',
97
92
  label: 'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
98
93
  },
99
94
  },
100
- composePlayerPrompt: (input) => composePlayerPrompt(input),
95
+ composePlayerPrompt: (input, promptIdentity) => composePlayerPrompt(input, promptIdentity),
101
96
  verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
102
97
  controlContextFields: [],
103
98
  transitionEventFields: [
@@ -3,18 +3,19 @@
3
3
  //
4
4
  // Generated by slc/link.md (FSM-to-Runtime linker).
5
5
  // Source FSM: ./review.fsm.ts
6
- // Player bind: Coder→coder, Reviewer→reviewer (default lowercased binding)
6
+ // Role metadata: Coder→coder, Reviewer→reviewer (canonical local roles)
7
7
  // Boss event: deterministic START_REVIEW entry; exact Boss text becomes
8
8
  // callerInput; pending player questions retain BOSS_REPLY
9
9
  // Adjudication: LLM judge per state; coderOutput and reviewerOutput are
10
10
  // carried verbatim
11
- // Compat: artifact schema 1 / runtime ABI 1
11
+ // Compat: artifact schema 2 / runtime ABI 1
12
12
 
13
13
  import {
14
14
  RUNTIME_ABI,
15
15
  createXStatePlaybookRuntime,
16
16
  snapshotJsonValue,
17
17
  type PlaybookPlayerInput,
18
+ type XStatePromptIdentity,
18
19
  type XStatePlaybookRuntimeSpec,
19
20
  } from '@sublang/playbook/xstate-runtime';
20
21
  import {
@@ -76,7 +77,6 @@ export type {
76
77
 
77
78
  export type ReviewPlaybookOptions = ReviewInput;
78
79
 
79
- const OPTION_KEYS = new Set(['coderLlm', 'reviewerLlm']);
80
80
  const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
81
81
  const CONTINUATION_PREAMBLE =
82
82
  'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
@@ -97,17 +97,7 @@ function snapshotReviewOptions(value: unknown): ReviewPlaybookOptions {
97
97
  }
98
98
  const record = captured as Readonly<Record<string, JsonValue>>;
99
99
  for (const key of Object.keys(record)) {
100
- if (!OPTION_KEYS.has(key)) {
101
- throw new TypeError(`REVIEW runtime options.${key} is not declared`);
102
- }
103
- }
104
- for (const key of OPTION_KEYS) {
105
- const option = record[key];
106
- if (typeof option !== 'string' || option.trim().length === 0) {
107
- throw new TypeError(
108
- `REVIEW runtime options.${key} must be a non-empty string`,
109
- );
110
- }
100
+ throw new TypeError(`REVIEW runtime options.${key} is not declared`);
111
101
  }
112
102
  return captured as unknown as ReviewPlaybookOptions;
113
103
  }
@@ -124,12 +114,20 @@ function quotedContinuation(value: string): string {
124
114
  }
125
115
 
126
116
  /** Keep every line of a relayed runtime value inside its authored quote. */
127
- function composePlayerPrompt(input: PlayerInput): string {
117
+ function composePlayerPrompt(
118
+ input: PlayerInput,
119
+ promptIdentity: XStatePromptIdentity,
120
+ ): string {
128
121
  const fields = input as unknown as Record<string, unknown>;
129
122
  const body = input.prompt.replace(
130
123
  PLACEHOLDER,
131
124
  (match: string, token: string, offset: number, source: string) => {
132
- const value = fields[placeholderField(token)];
125
+ const value =
126
+ token === 'coder-llm'
127
+ ? promptIdentity('coder')
128
+ : token === 'reviewer-llm'
129
+ ? promptIdentity('reviewer')
130
+ : fields[placeholderField(token)];
133
131
  if (typeof value !== 'string') return match;
134
132
  const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
135
133
  return source.slice(lineStart, offset) === '> '
@@ -156,34 +154,36 @@ export const _internal = {
156
154
  VERBATIM_PAYLOAD_FIELDS,
157
155
  };
158
156
 
159
- const runtimeSpec: XStatePlaybookRuntimeSpec<ReviewPlaybookOptions> = {
157
+ const runtimeSpec = {
160
158
  label: 'REVIEW',
161
- compat: { artifactSchema: 1, runtimeAbi: RUNTIME_ABI },
159
+ compat: { artifactSchema: 2, runtimeAbi: RUNTIME_ABI },
162
160
  snapshotOptions: snapshotReviewOptions,
163
161
  entryEvent: { type: 'START_REVIEW', textField: 'callerInput' },
164
- playerStates: {
162
+ roleStates: {
165
163
  reviewInitial: {
166
- player: 'Reviewer',
164
+ role: 'reviewer',
167
165
  label:
168
166
  'REVIEW-1: Reviewer examines the latest commit against the caller input.',
169
167
  },
170
168
  addressFindings: {
171
- player: 'Coder',
169
+ role: 'coder',
172
170
  label: 'REVIEW-2: Coder accepts or rejects every current review finding.',
173
171
  },
174
172
  reviewAfterCommit: {
175
- player: 'Reviewer',
173
+ role: 'reviewer',
176
174
  label:
177
175
  'REVIEW-3: Reviewer examines the new review-fix commit and repository state.',
178
176
  },
179
177
  reviewAfterRebuttal: {
180
- player: 'Reviewer',
178
+ role: 'reviewer',
181
179
  label:
182
180
  'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
183
181
  },
184
182
  },
185
- composePlayerPrompt: (input: PlaybookPlayerInput) =>
186
- composePlayerPrompt(input as PlayerInput),
183
+ composePlayerPrompt: (
184
+ input: PlaybookPlayerInput,
185
+ promptIdentity: XStatePromptIdentity,
186
+ ) => composePlayerPrompt(input as PlayerInput, promptIdentity),
187
187
  verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
188
188
  controlContextFields: [],
189
189
  transitionEventFields: [
@@ -193,7 +193,7 @@ const runtimeSpec: XStatePlaybookRuntimeSpec<ReviewPlaybookOptions> = {
193
193
  'answer',
194
194
  'questionId',
195
195
  ],
196
- };
196
+ } satisfies XStatePlaybookRuntimeSpec<ReviewPlaybookOptions>;
197
197
 
198
198
  const createPlaybookRuntime: PlaybookRuntimeFactory<ReviewPlaybookOptions> =
199
199
  createXStatePlaybookRuntime(reviewMachine, runtimeSpec);
@@ -1,4 +1,4 @@
1
- import { type PlaybookRuntime, type ReviewPlaybookOptions } from './review.playbook.js';
1
+ import { type PlaybookRuntime } from './review.playbook.js';
2
2
  export interface PlaybookSummaryPolicy {
3
3
  stateCountLabels: Readonly<Record<string, string>>;
4
4
  copyPasteGuardNames: readonly string[];
@@ -7,24 +7,17 @@ export interface PlaybookSummaryPolicy {
7
7
  copyPastes: number;
8
8
  }, rounds: number): string;
9
9
  }
10
- export interface RegistryPlayer {
11
- id: string;
12
- adapter?: string;
13
- model?: string;
14
- }
15
- export interface CreateReviewRuntimeOptions {
16
- captainOptions: unknown;
17
- players: readonly RegistryPlayer[];
18
- }
19
10
  export type ReviewOptions = Readonly<Record<string, never>>;
20
11
  export interface ReviewPlaybookRegistryEntry {
21
12
  id: 'review';
22
13
  command: 'review';
23
14
  intent: string;
15
+ artifactSchema: 2;
24
16
  requiredRoleIds: readonly ['coder', 'reviewer'];
17
+ concurrentRoleSets: readonly [];
25
18
  summaryPolicy: PlaybookSummaryPolicy;
26
- validateOptions(captainOptions: unknown): ReviewOptions;
27
- createRuntime(options: CreateReviewRuntimeOptions): PlaybookRuntime;
19
+ validateOptions(optionSlice: unknown): ReviewOptions;
20
+ createRuntime(options: ReviewOptions): PlaybookRuntime;
28
21
  }
29
22
  export declare const reviewStateCountLabels: {
30
23
  readonly reviewInitial: "review round";
@@ -38,6 +31,5 @@ export declare function reviewSavedCountsLine(counts: {
38
31
  }, rounds: number): string;
39
32
  export declare const reviewSummaryPolicy: PlaybookSummaryPolicy;
40
33
  export declare function validateReviewOptions(optionSlice: unknown): ReviewOptions;
41
- export declare function createReviewRuntimeOptions({ captainOptions, players, }: CreateReviewRuntimeOptions): ReviewPlaybookOptions;
42
34
  export declare const reviewPlaybookRegistryEntry: ReviewPlaybookRegistryEntry;
43
35
  export default reviewPlaybookRegistryEntry;
@@ -44,30 +44,17 @@ export function validateReviewOptions(optionSlice) {
44
44
  }
45
45
  return Object.freeze({});
46
46
  }
47
- function playerIdentity(players, id) {
48
- const player = players.find((entry) => entry.id === id);
49
- const identity = player?.model ?? player?.adapter;
50
- if (identity === undefined || identity.trim().length === 0) {
51
- throw new Error(`REVIEW requires configured player ${id}`);
52
- }
53
- return identity;
54
- }
55
- export function createReviewRuntimeOptions({ captainOptions, players, }) {
56
- validateReviewOptions(captainOptions);
57
- return {
58
- coderLlm: playerIdentity(players, 'coder'),
59
- reviewerLlm: playerIdentity(players, 'reviewer'),
60
- };
61
- }
62
47
  export const reviewPlaybookRegistryEntry = {
63
48
  id: 'review',
64
49
  command: 'review',
65
50
  intent: 'review the latest commit until no material correctness or spec findings remain',
51
+ artifactSchema: 2,
66
52
  requiredRoleIds: ['coder', 'reviewer'],
53
+ concurrentRoleSets: [],
67
54
  summaryPolicy: reviewSummaryPolicy,
68
55
  validateOptions: validateReviewOptions,
69
56
  createRuntime(options) {
70
- return createPlaybookRuntime(createReviewRuntimeOptions(options));
57
+ return createPlaybookRuntime(options);
71
58
  },
72
59
  };
73
60
  export default reviewPlaybookRegistryEntry;
@@ -3,7 +3,6 @@
3
3
 
4
4
  import createPlaybookRuntime, {
5
5
  type PlaybookRuntime,
6
- type ReviewPlaybookOptions,
7
6
  } from './review.playbook.js';
8
7
 
9
8
  export interface PlaybookSummaryPolicy {
@@ -15,27 +14,18 @@ export interface PlaybookSummaryPolicy {
15
14
  ): string;
16
15
  }
17
16
 
18
- export interface RegistryPlayer {
19
- id: string;
20
- adapter?: string;
21
- model?: string;
22
- }
23
-
24
- export interface CreateReviewRuntimeOptions {
25
- captainOptions: unknown;
26
- players: readonly RegistryPlayer[];
27
- }
28
-
29
17
  export type ReviewOptions = Readonly<Record<string, never>>;
30
18
 
31
19
  export interface ReviewPlaybookRegistryEntry {
32
20
  id: 'review';
33
21
  command: 'review';
34
22
  intent: string;
23
+ artifactSchema: 2;
35
24
  requiredRoleIds: readonly ['coder', 'reviewer'];
25
+ concurrentRoleSets: readonly [];
36
26
  summaryPolicy: PlaybookSummaryPolicy;
37
- validateOptions(captainOptions: unknown): ReviewOptions;
38
- createRuntime(options: CreateReviewRuntimeOptions): PlaybookRuntime;
27
+ validateOptions(optionSlice: unknown): ReviewOptions;
28
+ createRuntime(options: ReviewOptions): PlaybookRuntime;
39
29
  }
40
30
 
41
31
  export const reviewStateCountLabels = {
@@ -99,39 +89,18 @@ export function validateReviewOptions(optionSlice: unknown): ReviewOptions {
99
89
  return Object.freeze({});
100
90
  }
101
91
 
102
- function playerIdentity(
103
- players: readonly RegistryPlayer[],
104
- id: 'coder' | 'reviewer',
105
- ): string {
106
- const player = players.find((entry) => entry.id === id);
107
- const identity = player?.model ?? player?.adapter;
108
- if (identity === undefined || identity.trim().length === 0) {
109
- throw new Error(`REVIEW requires configured player ${id}`);
110
- }
111
- return identity;
112
- }
113
-
114
- export function createReviewRuntimeOptions({
115
- captainOptions,
116
- players,
117
- }: CreateReviewRuntimeOptions): ReviewPlaybookOptions {
118
- validateReviewOptions(captainOptions);
119
- return {
120
- coderLlm: playerIdentity(players, 'coder'),
121
- reviewerLlm: playerIdentity(players, 'reviewer'),
122
- };
123
- }
124
-
125
92
  export const reviewPlaybookRegistryEntry: ReviewPlaybookRegistryEntry = {
126
93
  id: 'review',
127
94
  command: 'review',
128
95
  intent:
129
96
  'review the latest commit until no material correctness or spec findings remain',
97
+ artifactSchema: 2,
130
98
  requiredRoleIds: ['coder', 'reviewer'],
99
+ concurrentRoleSets: [],
131
100
  summaryPolicy: reviewSummaryPolicy,
132
101
  validateOptions: validateReviewOptions,
133
102
  createRuntime(options) {
134
- return createPlaybookRuntime(createReviewRuntimeOptions(options));
103
+ return createPlaybookRuntime(options);
135
104
  },
136
105
  };
137
106
 
package/slc/gears2fsm.md CHANGED
@@ -36,7 +36,7 @@ map for every actor kind the GEARS artifact uses, using typed actor logic such
36
36
  as `fromPromise<Output, Input>(...)` [[11]]:
37
37
 
38
38
  - `captain` for direct work performed by Captain;
39
- - `player` for work Captain delegates to a named player;
39
+ - `player` for work Captain delegates to a named role;
40
40
  - `playbook` for a nested playbook call; and
41
41
  - `script` for a deterministic shell script an
42
42
  [optimizer-introduced script item](text2gears.md#script-behaviors-optimizer-introduced)
@@ -73,7 +73,7 @@ comment delimiters into a TypeScript target.
73
73
  `PlayerInput` shall be a typed object with at least:
74
74
 
75
75
  - `stateId`: the stable id of the invoking working leaf;
76
- - `player`: the [player](text2gears.md#players) Captain is to invoke;
76
+ - `role`: the canonical lowercase local id derived from the [role](text2gears.md#roles) Captain is to delegate;
77
77
  - `sourceItem`: the GEARS item ID this state realizes;
78
78
  - `prompt`: the source item's full final prompt, verbatim;
79
79
  - `result`: a record whose keys are the valid guard names this invocation may return.
@@ -244,22 +244,22 @@ Each state shall declare:
244
244
  - a stable `id` (for `#id` targeting and Boss interrupts);
245
245
  - an intuitive state key (the property name under `states: { ... }`);
246
246
  - a one-line `description` (for inspector tools and documentation);
247
- - JSON-safe `meta: { playbook: { stateId, description, player? } }` repeating
247
+ - JSON-safe `meta: { playbook: { stateId, description, role? } }` repeating
248
248
  its stable id and description so linked runtimes can discover active public
249
249
  identities through `snapshot.getMeta()` without private XState nodes. A
250
- delegated-player state shall also carry the exact source player in
251
- `meta.playbook.player`; every other state shall omit `player`;
250
+ delegated-role state shall also carry the canonical lowercase source role id in
251
+ `meta.playbook.role`; every other state shall omit `role`;
252
252
  - if it invokes the direct `captain` actor: `invoke.input` carrying
253
253
  `sourceItem`, `prompt`, and `result` (per [Setup](#setup));
254
254
  - if it invokes the delegated `player` actor: `invoke.input` additionally
255
- carrying the same source-derived `player` as `meta.playbook.player`;
255
+ carrying the same source-derived `role` as `meta.playbook.role`;
256
256
  - if it invokes the `script` actor: `invoke.input` carrying `stateId`,
257
257
  `sourceItem`, `command`, and `result` (per [Setup](#setup)) — no `prompt`
258
- and no `player`.
258
+ and no `role`.
259
259
 
260
260
  The source item ID shall live in `invoke.input.sourceItem`, not in a comment — this keeps the GEARS-to-state mapping machine-readable.
261
- A delegated state's `invoke.input.player` shall match its source item's named
262
- player. A direct Captain state shall not invent a `Captain` player binding.
261
+ A delegated state's `invoke.input.role` shall match the canonical lowercase id of its source item's named role.
262
+ A direct Captain state shall not invent a `Captain` role binding.
263
263
 
264
264
  Every invoking working leaf — sequential or parallel, whatever its actor
265
265
  kind — shall carry the tag `playbook.busy`: the shared quiescence helper
@@ -281,7 +281,7 @@ invoke: {
281
281
  src: 'player',
282
282
  input: ({ context }): PlayerInput => ({
283
283
  stateId: '<stable-state-id>',
284
- player: 'Reviewer',
284
+ role: 'reviewer',
285
285
  sourceItem: '<ITEM-A>',
286
286
  prompt: [
287
287
  'Flag any issues or improvements (numbered; no duplication).',
@@ -299,17 +299,17 @@ invoke: {
299
299
 
300
300
  For an item in which Captain acts directly, the corresponding invocation uses
301
301
  `src: 'captain'` and a `CaptainInput` with the same static mapping fields but no
302
- `player` field.
302
+ `role` field.
303
303
 
304
304
  ## Mapping
305
305
 
306
306
  Each Source spec item shall map to exactly one state in Target.
307
307
  A state's `invoke.input.sourceItem` shall be that item's ID, and `invoke.input.prompt` shall carry the item's prompt verbatim.
308
308
 
309
- An item written as direct Captain work shall map to exactly one `captain`
310
- invocation. An item that prompts or relays to a named player shall map to
311
- exactly one `player` invocation. A nested-call item shall map to exactly one
312
- `playbook` invocation. A script item (`Captain shall run:`) shall map to
309
+ An item written as direct Captain work shall map to exactly one `captain` invocation.
310
+ An item that prompts or relays to a named role shall map to exactly one `player` invocation.
311
+ A nested-call item shall map to exactly one `playbook` invocation.
312
+ A script item (`Captain shall run:`) shall map to
313
313
  exactly one `script` invocation whose `input.command` carries the blockquote
314
314
  verbatim and whose `result` preserves the item's two guards in declared order.
315
315
  The compiler shall not infer one actor kind from a
@@ -333,10 +333,14 @@ Each member shall be a delegated-player item; a direct-Captain or nested-call
333
333
  member is malformed because those actor kinds share one Captain control lane or one
334
334
  pending-child slot. Each region shall contain a delegated-player working leaf
335
335
  and a local final state; the working leaf retains the item's stable state id,
336
- `sourceItem`, player, prompt, and result contract.
336
+ `sourceItem`, role, prompt, and result contract.
337
+ The members' canonical role ids shall be pairwise distinct; a repeated role in one group is malformed.
337
338
  The parallel parent shall use `onDone` as the join, which XState takes only
338
339
  after every region reaches final.
339
340
 
341
+ The artifact shall export `concurrentRoleSets` as a deeply readonly array containing one role-id array per parallel group in first-item source order, with each inner array following that group's item order.
342
+ An artifact with no parallel group shall export an empty array.
343
+
340
344
  Each branch shall assign only its own staged result.
341
345
  The join shall promote all staged results atomically before later work begins,
342
346
  so branch completion order cannot change downstream inputs.
@@ -527,8 +531,8 @@ the number of sequential child calls without an arbitrary runtime call limit.
527
531
  Prompts shall pass only the **specific extracted fields** the player needs.
528
532
  The compiler shall not dump `JSON.stringify(lastResult)` or any opaque blob: it leaks internal `guard` strings, wastes tokens, and confuses the LLM.
529
533
 
530
- Player bindings and per-run parameters shall flow in via the machine's `input` and be copied into context at start-up.
531
- The artifact shall not bake in player bindings, model names, or per-run values.
534
+ Player bindings and prompt identities shall enter only through `PlaybookSession.roleBindings` at runtime call, prompt, and trace boundaries.
535
+ The artifact shall not bake them into machine input, options, or context; model names and other host settings shall remain host policy rather than persisted FSM state.
532
536
  Host-owned configuration such as an enabled-playbook catalog shall remain
533
537
  immutable machine input/context for the session. Boss events and actor outputs
534
538
  shall not carry, replace, append to, or otherwise overwrite that catalog.
@@ -585,7 +589,7 @@ Phases may set typed routing fields so terminal outcomes return to the originati
585
589
 
586
590
  ## Boss control
587
591
 
588
- [Boss](text2gears.md#players) input enters the machine through three surfaces: pre-emptive interrupts on active states, typed entry events on idle or recoverable states, and Boss replies to player questions that suspended the FSM in a dedicated wait state.
592
+ [Boss](text2gears.md#roles) input enters the machine through three surfaces: pre-emptive interrupts on active states, typed entry events on idle or recoverable states, and Boss replies to delegated-role questions that suspended the FSM in a dedicated wait state.
589
593
 
590
594
  ### Boss interrupts
591
595
 
@@ -667,12 +671,12 @@ the colon as the JSON field name.
667
671
  The linked runtime composes player prompts per [link.md "Player prompt composition"](link.md#player-prompt-composition), without adding a player-visible Boss-question instruction.
668
672
 
669
673
  The question record shall be
670
- `{ questionId, resumeStateId, sourceItem, player, question }`.
674
+ `{ questionId, resumeStateId, sourceItem, asker, question }`, where `asker` is exactly `{ kind: 'captain' }` or `{ kind: 'role', roleId }`.
671
675
  `questionId` and `resumeStateId` shall both equal the stable working-leaf
672
676
  `stateId`.
673
- `questionId`, `resumeStateId`, and `sourceItem` shall come from the suspended
674
- working leaf's stable invocation metadata. `player` shall come from a delegated
675
- `PlayerInput`, or be the literal `Captain` for a direct-Captain state. Only
677
+ `questionId`, `resumeStateId`, and `sourceItem` shall come from the suspended working leaf's stable invocation metadata.
678
+ A delegated `PlayerInput` shall produce the role asker with its canonical local role id, while a direct-Captain state shall produce the Captain asker without inventing a role.
679
+ Only
676
680
  `question` shall come from adjudicated actor output.
677
681
 
678
682
  A machine with at most one active Captain or player task may use the scalar