@sublang/playbook 15.1.0 → 16.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 (76) hide show
  1. package/docs/embedding.md +2 -2
  2. package/package.json +1 -1
  3. package/reference/sdlc/branch.playbook/branch.fsm.d.ts +63 -33
  4. package/reference/sdlc/branch.playbook/branch.fsm.js +190 -108
  5. package/reference/sdlc/branch.playbook/branch.fsm.ts +269 -147
  6. package/reference/sdlc/branch.playbook/branch.gears.md +7 -17
  7. package/reference/sdlc/branch.playbook/branch.playbook.d.ts +12 -16
  8. package/reference/sdlc/branch.playbook/branch.playbook.js +112 -101
  9. package/reference/sdlc/branch.playbook/branch.playbook.ts +128 -171
  10. package/reference/sdlc/branch.playbook/branch.registry.d.ts +3 -2
  11. package/reference/sdlc/branch.playbook/branch.registry.ts +6 -2
  12. package/reference/sdlc/code.playbook/code.fsm.d.ts +143 -90
  13. package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +8 -8
  14. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +13 -13
  15. package/reference/sdlc/code.playbook/code.fsm.js +609 -649
  16. package/reference/sdlc/code.playbook/code.fsm.ts +890 -821
  17. package/reference/sdlc/code.playbook/code.gears.md +39 -29
  18. package/reference/sdlc/code.playbook/code.playbook.d.ts +13 -16
  19. package/reference/sdlc/code.playbook/code.playbook.js +166 -147
  20. package/reference/sdlc/code.playbook/code.playbook.ts +180 -229
  21. package/reference/sdlc/code.playbook/code.registry.d.ts +3 -2
  22. package/reference/sdlc/code.playbook/code.registry.ts +6 -2
  23. package/reference/sdlc/code.playbook/playbook-captain.d.ts +42 -0
  24. package/reference/sdlc/code.playbook/playbook-captain.js +829 -194
  25. package/reference/sdlc/code.playbook/playbook-captain.ts +1227 -220
  26. package/reference/sdlc/decide.md +1 -0
  27. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +270 -154
  28. package/reference/sdlc/decide.playbook/decide.fsm.js +765 -732
  29. package/reference/sdlc/decide.playbook/decide.fsm.ts +1167 -945
  30. package/reference/sdlc/decide.playbook/decide.gears.md +28 -21
  31. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +11 -59
  32. package/reference/sdlc/decide.playbook/decide.playbook.js +155 -3696
  33. package/reference/sdlc/decide.playbook/decide.playbook.ts +186 -5244
  34. package/reference/sdlc/decide.playbook/decide.registry.d.ts +8 -4
  35. package/reference/sdlc/decide.playbook/decide.registry.js +2 -2
  36. package/reference/sdlc/decide.playbook/decide.registry.ts +13 -6
  37. package/reference/sdlc/dev.playbook/dev.fsm.d.ts +113 -131
  38. package/reference/sdlc/dev.playbook/dev.fsm.js +517 -575
  39. package/reference/sdlc/dev.playbook/dev.fsm.ts +667 -787
  40. package/reference/sdlc/dev.playbook/dev.gears.md +52 -54
  41. package/reference/sdlc/dev.playbook/dev.playbook.d.ts +13 -10
  42. package/reference/sdlc/dev.playbook/dev.playbook.js +128 -120
  43. package/reference/sdlc/dev.playbook/dev.playbook.ts +143 -193
  44. package/reference/sdlc/dev.playbook/dev.registry.d.ts +3 -2
  45. package/reference/sdlc/dev.playbook/dev.registry.ts +6 -2
  46. package/reference/sdlc/pr.playbook/pr.fsm.d.ts +92 -85
  47. package/reference/sdlc/pr.playbook/pr.fsm.js +461 -518
  48. package/reference/sdlc/pr.playbook/pr.fsm.ts +533 -663
  49. package/reference/sdlc/pr.playbook/pr.gears.md +47 -63
  50. package/reference/sdlc/pr.playbook/pr.playbook.d.ts +13 -23
  51. package/reference/sdlc/pr.playbook/pr.playbook.js +118 -112
  52. package/reference/sdlc/pr.playbook/pr.playbook.ts +136 -190
  53. package/reference/sdlc/pr.playbook/pr.registry.d.ts +3 -2
  54. package/reference/sdlc/pr.playbook/pr.registry.ts +4 -2
  55. package/reference/sdlc/review.md +3 -3
  56. package/reference/sdlc/review.playbook/review.fsm.d.ts +150 -116
  57. package/reference/sdlc/review.playbook/review.fsm.js +479 -550
  58. package/reference/sdlc/review.playbook/review.fsm.ts +717 -662
  59. package/reference/sdlc/review.playbook/review.gears.md +32 -29
  60. package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -12
  61. package/reference/sdlc/review.playbook/review.playbook.js +180 -155
  62. package/reference/sdlc/review.playbook/review.playbook.ts +190 -230
  63. package/reference/sdlc/review.playbook/review.registry.d.ts +6 -5
  64. package/reference/sdlc/review.playbook/review.registry.js +3 -3
  65. package/reference/sdlc/review.playbook/review.registry.ts +9 -5
  66. package/slc/gears2fsm.md +72 -3
  67. package/slc/link.md +50 -27
  68. package/slc/materialize-link.mjs +51 -13
  69. package/slc/optimize.md +4 -1
  70. package/slc/prefix-prompts.mjs +29 -28
  71. package/slc/prefix.md +27 -24
  72. package/slc/slc.pin-inputs.json +11 -2
  73. package/slc/text2gears.md +71 -11
  74. package/src/xstate-playbook-runtime.d.ts +54 -15
  75. package/src/xstate-playbook-runtime.js +1154 -248
  76. package/src/xstate-playbook-runtime.ts +1603 -317
@@ -1,288 +1,248 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
- //
4
- // Generated by slc/link.md (FSM-to-Runtime linker).
5
- // Source FSM: ./review.fsm.ts
6
- // Role metadata: Coder→coder, Reviewer→reviewer (canonical local roles)
7
- // Boss event: deterministic START_REVIEW entry; exact Boss text becomes
8
- // callerInput; pending player questions retain BOSS_REPLY
9
- // Adjudication: LLM judge per state; coderOutput and reviewerOutput are
10
- // carried verbatim; latestCommit is receipt-owned effect
11
- // evidence, never taken from either player's prose
12
- // Compat: artifact schema 3 / runtime ABI 1
13
-
3
+ // Generated by slc/materialize-link.mjs; governed by the complete slc/link.md.
4
+ // FSM: "./review.fsm.js"; export: reviewMachine; strategies: shared defaults with labelled string relays and nested actors.
5
+ import { reviewMachine as machine } from "./review.fsm.js";
6
+ import type { PlayerInput as SourcePlayerInput } from "./review.fsm.js";
14
7
  import {
15
- createXStatePlaybookRuntime,
16
- composePlayerContinuation,
17
- snapshotJsonValue,
8
+ createXStatePlaybookRuntime, snapshotJsonValue,
9
+ composePlayerContinuation, type XStatePromptIdentity,
18
10
  type PlaybookPlayerInput,
19
- type XStatePlaybookRuntimeFactory,
20
11
  type XStatePlaybookRuntimeConstruction,
21
- type XStatePromptIdentity,
12
+ type XStatePlaybookRuntimeFactory,
22
13
  type XStatePlaybookRuntimeSpecV3,
23
14
  } from '@sublang/playbook/xstate-runtime';
24
- import {
25
- reviewMachine,
26
- type PlayerInput,
27
- type ReviewInput,
28
- } from './review.fsm.js';
29
- import type { PlaybookHostConstructionCapabilities } from '../code.playbook/playbook-captain.js';
30
- import type {
31
- CaptainCallOptions,
32
- CaptainResult,
33
- JsonValue,
34
- NormalizedError,
35
- PlayerCallOptions,
36
- PlaybookCallRequest,
37
- PlaybookCallResult,
38
- PlaybookCallStart,
39
- PlaybookControlReceipt,
40
- PlaybookControlView,
41
- PlaybookPendingCall,
42
- PlaybookPorts,
43
- PlaybookRunResult,
44
- PlaybookRuntime,
45
- PlaybookRuntimeFactory,
46
- PlaybookRuntimeSnapshot,
47
- PlaybookSession,
48
- PlaybookState,
49
- PlaybookStateValue,
50
- PlaybookTraceEvent,
51
- PlaybookTraceType,
15
+ export type {
52
16
  PlayerResult,
17
+ PlayerCallOptions,
53
18
  PlayerSessionStore,
54
- } from '@sublang/playbook/runtime';
55
-
56
- export type {
57
- CaptainCallOptions,
58
19
  CaptainResult,
20
+ CaptainCallOptions,
59
21
  JsonValue,
60
22
  NormalizedError,
61
- PlayerCallOptions,
62
23
  PlaybookCallRequest,
63
24
  PlaybookCallResult,
64
25
  PlaybookCallStart,
65
- PlaybookControlReceipt,
66
- PlaybookControlView,
26
+ PlaybookStateValue,
27
+ PlaybookState,
67
28
  PlaybookPendingCall,
68
- PlaybookPorts,
69
29
  PlaybookRunResult,
70
30
  PlaybookRuntime,
71
31
  PlaybookRuntimeFactory,
72
32
  PlaybookRuntimeSnapshot,
73
33
  PlaybookSession,
74
- PlaybookState,
75
- PlaybookStateValue,
34
+ PlaybookPorts,
76
35
  PlaybookTraceEvent,
77
36
  PlaybookTraceType,
78
- PlayerResult,
79
- PlayerSessionStore,
80
- };
37
+ PlaybookControlReceipt,
38
+ PlaybookControlView,
39
+ } from '@sublang/playbook/runtime';
81
40
 
82
- export type ReviewPlaybookOptions = ReviewInput;
83
- export type ReviewPlaybookHostCapabilities =
84
- PlaybookHostConstructionCapabilities &
85
- XStatePlaybookRuntimeConstruction<ReviewPlaybookOptions, object>['hostCapabilities'];
41
+ export interface PlaybookRuntimeOptions {
86
42
 
87
- const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
43
+ }
44
+ export type PlaybookHostCapabilities = XStatePlaybookRuntimeConstruction<
45
+ PlaybookRuntimeOptions, { readonly authority: object }
46
+ >['hostCapabilities'];
88
47
 
48
+ const OPTION_SCHEMA = {} as const;
49
+ const INPUT_MAPPING = {} as const;
50
+ const RESUMABLE_STATE_IDS: ReadonlySet<string> = new Set([
51
+ "firstReview",
52
+ "fixFindings",
53
+ "reviewAfterFix",
54
+ "reviewAfterRejection"
55
+ ]);
56
+ const UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string> = new Set([]);
89
57
  const VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string> = new Set([
90
- 'reviewerOutput',
91
- 'coderOutput',
58
+ "reviewerOutput",
59
+ "coderOutput"
92
60
  ]);
93
- const UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string> = new Set();
94
-
95
- function snapshotReviewOptions(value: unknown): ReviewPlaybookOptions {
96
- const captured = snapshotJsonValue(value, 'REVIEW runtime options');
97
- if (
98
- captured === null ||
99
- typeof captured !== 'object' ||
100
- Array.isArray(captured)
101
- ) {
102
- throw new TypeError('REVIEW runtime options must be an object');
61
+ export function validateOptions(value: unknown): PlaybookRuntimeOptions {
62
+ const captured = snapshotJsonValue(value === undefined ? {} : value, "REVIEW runtime options");
63
+ if (captured === null || typeof captured !== 'object' || Array.isArray(captured)) {
64
+ throw new TypeError("REVIEW runtime options must be an object");
103
65
  }
104
- const record = captured as Readonly<Record<string, JsonValue>>;
105
- for (const key of Object.keys(record)) {
106
- throw new TypeError(`REVIEW runtime options.${key} is not declared`);
66
+ const fields = captured as Readonly<Record<string, unknown>>;
67
+ for (const key of Object.keys(captured)) {
68
+ if (!Object.hasOwn(OPTION_SCHEMA, key)) throw new TypeError("REVIEW runtime options." + key + ' is not declared');
107
69
  }
108
- return captured as unknown as ReviewPlaybookOptions;
109
- }
110
-
111
- function placeholderField(token: string): string {
112
- if (token === '#') return 'irNumber';
113
- return token.replace(/-([A-Za-z0-9_$])/g, (_match, next: string) =>
114
- next.toUpperCase(),
115
- );
116
- }
117
-
118
- function quotedContinuation(value: string): string {
119
- return value.replaceAll('\n', '\n> ');
70
+ for (const [key, schema] of Object.entries(OPTION_SCHEMA) as [string, { type: string; required: boolean }][]) {
71
+ if (!Object.hasOwn(captured, key)) {
72
+ if (schema.required) throw new TypeError("REVIEW runtime options." + key + ' is required');
73
+ } else if (typeof fields[key] !== schema.type || (schema.type === 'number' && !Number.isFinite(fields[key]))) {
74
+ throw new TypeError("REVIEW runtime options." + key + ' must be a ' + schema.type);
75
+ }
76
+ }
77
+ return captured as unknown as PlaybookRuntimeOptions;
120
78
  }
121
-
122
- /** Keep every line of a relayed runtime value inside its authored quote. */
123
- function composePlayerPrompt(
124
- input: PlayerInput,
125
- promptIdentity: XStatePromptIdentity,
126
- resuming = false,
127
- ): string {
128
- const fields = input as unknown as Record<string, unknown>;
129
- const body = input.prompt.replace(
130
- PLACEHOLDER,
131
- (match: string, token: string, offset: number, source: string) => {
132
- const value =
133
- token === 'coder-llm'
134
- ? promptIdentity('coder')
135
- : token === 'reviewer-llm'
136
- ? promptIdentity('reviewer')
137
- : fields[placeholderField(token)];
79
+ function renderPlayerPrompt(input: SourcePlayerInput, promptIdentity: XStatePromptIdentity, resuming = false): string {
80
+ const fields = input as unknown as Readonly<Record<string, unknown>>;
81
+ const mapping: Readonly<Record<string, string>> = {};
82
+ const identities: Readonly<Record<string, string>> = {"coder-llm":"coder","reviewer-llm":"reviewer"};
83
+ const omitted = new Set<string>([]);
84
+ const fieldFor = (token: string): string => Object.hasOwn(mapping, token) ? mapping[token]! : (token === '#' ? 'irNumber' : token.replace(/-([A-Za-z0-9_$])/g, (_match: string, next: string) => next.toUpperCase()));
85
+ const parts = input.prompt.split(/(\r?\n)/);
86
+ let body = '';
87
+ for (let index = 0; index < parts.length; index += 2) {
88
+ const line = parts[index]!;
89
+ if (omitted.has(line)) {
90
+ const token = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>$/.exec(line)![1]!;
91
+ if (fields[fieldFor(token)] === '') continue;
92
+ }
93
+ body += line.replace(/<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g, (match: string, token: string): string => {
94
+ const value = Object.hasOwn(identities, token) ? promptIdentity(identities[token]!) : fields[fieldFor(token)];
138
95
  if (typeof value !== 'string') return match;
139
- const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
140
- return source.slice(lineStart, offset).startsWith('> ')
141
- ? quotedContinuation(value)
142
- : value;
143
- },
144
- );
96
+ return line.startsWith('> ') ? value.replace(/\r?\n/g, (ending: string) => ending + '> ') : value;
97
+ }) + (parts[index + 1] ?? '');
98
+ }
145
99
  return composePlayerContinuation(input, body, resuming);
146
100
  }
147
-
148
- export const _internal = {
149
- composePlayerPrompt,
150
- VERBATIM_PAYLOAD_FIELDS,
151
- UNFINISHED_FINAL_STATE_IDS,
152
- };
153
-
101
+ const composePlayerPrompt = (input: PlaybookPlayerInput, promptIdentity: XStatePromptIdentity, resuming?: boolean): string =>
102
+ renderPlayerPrompt(input as SourcePlayerInput, promptIdentity, resuming);
154
103
  const runtimeSpec = {
155
- label: 'REVIEW',
156
- // DR-022 / slc/link.md: the declaration carries the value current at link
157
- // time — a literal, never the loading engine's RUNTIME_ABI self-report,
158
- // which would follow whatever engine loads the module and make the
159
- // factory's skew check compare that engine with itself.
160
- compat: { artifactSchema: 3, runtimeAbi: 1 },
161
- snapshotOptions: snapshotReviewOptions,
162
- entryEvent: {
163
- type: 'START_REVIEW',
164
- textField: 'callerInput',
165
- // `copyStartInput` copies the entry text here, so the failure-state
166
- // retry reads it back from the persisted machine snapshot and survives
167
- // a continued session (DR-034).
168
- contextField: 'callerInput',
104
+ composePlayerPrompt,
105
+ ...{
106
+ "label": "REVIEW",
107
+ "compat": {
108
+ "artifactSchema": 3,
109
+ "runtimeAbi": 1
169
110
  },
170
- roleStates: {
171
- reviewInitial: {
172
- role: 'reviewer',
173
- label:
174
- "REVIEW-1: Reviewer reviews the caller's review scope against the original intent.",
175
- },
176
- addressFindings: {
177
- role: 'coder',
178
- label: 'REVIEW-2: Coder accepts or rejects every current review finding.',
111
+ "entryEvent": {
112
+ "type": "START_REVIEW",
113
+ "textField": "callerInput",
114
+ "contextField": "callerInput"
115
+ },
116
+ "bossEvents": [],
117
+ "roleStates": {
118
+ "firstReview": {
119
+ "role": "reviewer",
120
+ "label": "Reviewer runs the first review round over the review scope."
179
121
  },
180
- reviewAfterCommit: {
181
- role: 'reviewer',
182
- label:
183
- 'REVIEW-3: Reviewer reviews the cumulative committed state after a review-fix commit.',
122
+ "fixFindings": {
123
+ "role": "coder",
124
+ "label": "Coder accepts or rejects each of Reviewer's findings and commits any accepted fixes."
184
125
  },
185
- reviewAfterRebuttal: {
186
- role: 'reviewer',
187
- label:
188
- 'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
126
+ "reviewAfterFix": {
127
+ "role": "reviewer",
128
+ "label": "Reviewer runs the next review round over the cumulative committed state after a review-fix commit."
189
129
  },
130
+ "reviewAfterRejection": {
131
+ "role": "reviewer",
132
+ "label": "Reviewer runs the next review round after Coder rejected every finding without a commit."
133
+ }
190
134
  },
191
- outcomeAuthority: {
192
- governedPlayerStates: {
193
- reviewInitial: {
194
- hasFindings: {
195
- fields: { reviewerOutput: 'presentation' },
196
- repositoryDisposition: 'unchanged',
197
- },
198
- noFindings: {
199
- // DR-045: the clean round's unchanged receipt observes the exact
200
- // evaluated revision as HEAD; the reconciler injects it here.
201
- fields: { evaluatedRevision: 'effect' },
202
- repositoryDisposition: 'unchanged',
135
+ "outcomeAuthority": {
136
+ "governedPlayerStates": {
137
+ "firstReview": {
138
+ "hasFindings": {
139
+ "fields": {
140
+ "reviewerOutput": "presentation"
141
+ },
142
+ "repositoryDisposition": "unchanged"
203
143
  },
204
- needsBossReply: {
205
- fields: { question: 'presentation' },
206
- repositoryDisposition: 'unchanged',
144
+ "noFindings": {
145
+ "fields": {
146
+ "evaluatedRevision": "effect"
147
+ },
148
+ "repositoryDisposition": "unchanged"
207
149
  },
150
+ "needsBossReply": {
151
+ "fields": {
152
+ "question": "presentation"
153
+ },
154
+ "repositoryDisposition": "unchanged"
155
+ }
208
156
  },
209
- addressFindings: {
210
- committed: {
211
- fields: {
212
- coderOutput: 'presentation',
213
- latestCommit: 'effect',
157
+ "fixFindings": {
158
+ "committed": {
159
+ "fields": {
160
+ "coderOutput": "presentation",
161
+ "latestCommit": "effect"
214
162
  },
215
- repositoryDisposition: 'one-descendant-commit',
216
- },
217
- rejectedAll: {
218
- fields: { coderOutput: 'presentation' },
219
- repositoryDisposition: 'unchanged',
163
+ "repositoryDisposition": "one-descendant-commit"
220
164
  },
221
- needsBossReply: {
222
- fields: { question: 'presentation' },
223
- repositoryDisposition: 'deferred',
165
+ "rejectedAll": {
166
+ "fields": {
167
+ "coderOutput": "presentation"
168
+ },
169
+ "repositoryDisposition": "unchanged"
224
170
  },
171
+ "needsBossReply": {
172
+ "fields": {
173
+ "question": "presentation"
174
+ },
175
+ "repositoryDisposition": "deferred"
176
+ }
225
177
  },
226
- reviewAfterCommit: {
227
- hasFindings: {
228
- fields: { reviewerOutput: 'presentation' },
229
- repositoryDisposition: 'unchanged',
230
- },
231
- noFindings: {
232
- // DR-045: the clean round's unchanged receipt observes the exact
233
- // evaluated revision as HEAD; the reconciler injects it here.
234
- fields: { evaluatedRevision: 'effect' },
235
- repositoryDisposition: 'unchanged',
178
+ "reviewAfterFix": {
179
+ "hasFindings": {
180
+ "fields": {
181
+ "reviewerOutput": "presentation"
182
+ },
183
+ "repositoryDisposition": "unchanged"
236
184
  },
237
- needsBossReply: {
238
- fields: { question: 'presentation' },
239
- repositoryDisposition: 'unchanged',
185
+ "noFindings": {
186
+ "fields": {
187
+ "evaluatedRevision": "effect"
188
+ },
189
+ "repositoryDisposition": "unchanged"
240
190
  },
191
+ "needsBossReply": {
192
+ "fields": {
193
+ "question": "presentation"
194
+ },
195
+ "repositoryDisposition": "unchanged"
196
+ }
241
197
  },
242
- reviewAfterRebuttal: {
243
- hasFindings: {
244
- fields: { reviewerOutput: 'presentation' },
245
- repositoryDisposition: 'unchanged',
246
- },
247
- noFindings: {
248
- // DR-045: the clean round's unchanged receipt observes the exact
249
- // evaluated revision as HEAD; the reconciler injects it here.
250
- fields: { evaluatedRevision: 'effect' },
251
- repositoryDisposition: 'unchanged',
198
+ "reviewAfterRejection": {
199
+ "hasFindings": {
200
+ "fields": {
201
+ "reviewerOutput": "presentation"
202
+ },
203
+ "repositoryDisposition": "unchanged"
252
204
  },
253
- needsBossReply: {
254
- fields: { question: 'presentation' },
255
- repositoryDisposition: 'unchanged',
205
+ "noFindings": {
206
+ "fields": {
207
+ "evaluatedRevision": "effect"
208
+ },
209
+ "repositoryDisposition": "unchanged"
256
210
  },
257
- },
258
- },
211
+ "needsBossReply": {
212
+ "fields": {
213
+ "question": "presentation"
214
+ },
215
+ "repositoryDisposition": "unchanged"
216
+ }
217
+ }
218
+ }
259
219
  },
260
- composePlayerPrompt: (
261
- input: PlaybookPlayerInput,
262
- promptIdentity: XStatePromptIdentity,
263
- resuming?: boolean,
264
- ) => composePlayerPrompt(input as PlayerInput, promptIdentity, resuming),
220
+ "placeholderFields": {},
221
+ "transitionEventFields": [
222
+ "callerInput",
223
+ "targetId",
224
+ "answer",
225
+ "questionId"
226
+ ],
227
+ "controlContextFields": []
228
+ },
229
+ snapshotOptions: validateOptions,
230
+ machineInput: (options: PlaybookRuntimeOptions) => Object.fromEntries(
231
+ Object.entries(INPUT_MAPPING).filter(([, key]) => options[key as keyof PlaybookRuntimeOptions] !== undefined)
232
+ .map(([field, key]) => [field, options[key as keyof PlaybookRuntimeOptions]]),
233
+ ),
265
234
  verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
266
- controlContextFields: [],
235
+ resumableStateIds: RESUMABLE_STATE_IDS,
267
236
  unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
268
- transitionEventFields: [
269
- 'callerInput',
270
- 'bossIntent',
271
- 'targetId',
272
- 'answer',
273
- 'questionId',
274
- ],
275
- } satisfies XStatePlaybookRuntimeSpecV3<ReviewPlaybookOptions>;
237
+ } satisfies XStatePlaybookRuntimeSpecV3<PlaybookRuntimeOptions>;
276
238
 
239
+ export const _internal = {
240
+ composePlayerPrompt,
241
+ RESUMABLE_STATE_IDS,
242
+ UNFINISHED_FINAL_STATE_IDS,
243
+ VERBATIM_PAYLOAD_FIELDS,
244
+ };
277
245
  const createPlaybookRuntime: XStatePlaybookRuntimeFactory<
278
- XStatePlaybookRuntimeConstruction<
279
- ReviewPlaybookOptions,
280
- ReviewPlaybookHostCapabilities
281
- >,
282
- 3
283
- > = createXStatePlaybookRuntime<
284
- ReviewPlaybookOptions,
285
- ReviewPlaybookHostCapabilities
286
- >(reviewMachine, runtimeSpec);
287
-
246
+ XStatePlaybookRuntimeConstruction<PlaybookRuntimeOptions, PlaybookHostCapabilities>, 3
247
+ > = createXStatePlaybookRuntime<PlaybookRuntimeOptions, PlaybookHostCapabilities>(machine, runtimeSpec);
288
248
  export default createPlaybookRuntime;
@@ -1,4 +1,5 @@
1
- import { type ReviewPlaybookHostCapabilities, type PlaybookRuntime } from './review.playbook.js';
1
+ import { type PlaybookHostCapabilities, type PlaybookRuntime } from './review.playbook.js';
2
+ import type { PlaybookHostConstructionCapabilities } from '../code.playbook/playbook-captain.js';
2
3
  export interface PlaybookSummaryPolicy {
3
4
  stateCountLabels: Readonly<Record<string, string>>;
4
5
  copyPasteGuardNames: readonly string[];
@@ -24,12 +25,12 @@ export interface ReviewPlaybookRegistryEntry {
24
25
  concurrentRoleSets: readonly [];
25
26
  summaryPolicy: PlaybookSummaryPolicy;
26
27
  validateOptions(optionSlice: unknown): ReviewOptions;
27
- createRuntime(options: ReviewOptions, hostCapabilities: ReviewPlaybookHostCapabilities): PlaybookRuntime;
28
+ createRuntime(options: ReviewOptions, hostCapabilities: PlaybookHostConstructionCapabilities & PlaybookHostCapabilities): PlaybookRuntime;
28
29
  }
29
30
  export declare const reviewStateCountLabels: {
30
- readonly reviewInitial: "review round";
31
- readonly reviewAfterCommit: "review round";
32
- readonly reviewAfterRebuttal: "rebuttal";
31
+ readonly firstReview: "review round";
32
+ readonly reviewAfterFix: "review round";
33
+ readonly reviewAfterRejection: "rebuttal";
33
34
  };
34
35
  export declare const reviewCopyPasteGuardNames: readonly ["hasFindings", "committed", "rejectedAll"];
35
36
  export declare function reviewSavedCountsLine(counts: {
@@ -2,9 +2,9 @@
2
2
  // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
3
  import createPlaybookRuntime from './review.playbook.js';
4
4
  export const reviewStateCountLabels = {
5
- reviewInitial: 'review round',
6
- reviewAfterCommit: 'review round',
7
- reviewAfterRebuttal: 'rebuttal',
5
+ firstReview: 'review round',
6
+ reviewAfterFix: 'review round',
7
+ reviewAfterRejection: 'rebuttal',
8
8
  };
9
9
  export const reviewCopyPasteGuardNames = [
10
10
  'hasFindings',
@@ -2,9 +2,10 @@
2
2
  // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
3
 
4
4
  import createPlaybookRuntime, {
5
- type ReviewPlaybookHostCapabilities,
5
+ type PlaybookHostCapabilities,
6
6
  type PlaybookRuntime,
7
7
  } from './review.playbook.js';
8
+ import type { PlaybookHostConstructionCapabilities } from '../code.playbook/playbook-captain.js';
8
9
 
9
10
  export interface PlaybookSummaryPolicy {
10
11
  stateCountLabels: Readonly<Record<string, string>>;
@@ -33,16 +34,19 @@ export interface ReviewPlaybookRegistryEntry {
33
34
  concurrentRoleSets: readonly [];
34
35
  summaryPolicy: PlaybookSummaryPolicy;
35
36
  validateOptions(optionSlice: unknown): ReviewOptions;
37
+ // The linked module types live authority as opaque (link-materialization);
38
+ // the Captain entry binds its own construction capabilities here.
36
39
  createRuntime(
37
40
  options: ReviewOptions,
38
- hostCapabilities: ReviewPlaybookHostCapabilities,
41
+ hostCapabilities: PlaybookHostConstructionCapabilities &
42
+ PlaybookHostCapabilities,
39
43
  ): PlaybookRuntime;
40
44
  }
41
45
 
42
46
  export const reviewStateCountLabels = {
43
- reviewInitial: 'review round',
44
- reviewAfterCommit: 'review round',
45
- reviewAfterRebuttal: 'rebuttal',
47
+ firstReview: 'review round',
48
+ reviewAfterFix: 'review round',
49
+ reviewAfterRejection: 'rebuttal',
46
50
  } as const;
47
51
 
48
52
  export const reviewCopyPasteGuardNames = [