@sublang/playbook 4.0.0 → 6.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 (73) hide show
  1. package/README.md +69 -122
  2. package/docs/assets/playbook-venn.svg +13 -0
  3. package/docs/cli.md +43 -26
  4. package/docs/configuration.md +63 -18
  5. package/docs/embedding.md +24 -16
  6. package/package.json +43 -22
  7. package/reference/sdlc/captain.md +70 -83
  8. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +127 -142
  9. package/reference/sdlc/captain.playbook/captain.fsm.js +349 -470
  10. package/reference/sdlc/captain.playbook/captain.fsm.ts +535 -598
  11. package/reference/sdlc/captain.playbook/captain.gears.md +37 -41
  12. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +90 -15
  13. package/reference/sdlc/captain.playbook/captain.playbook.js +466 -976
  14. package/reference/sdlc/captain.playbook/captain.playbook.ts +698 -1001
  15. package/reference/sdlc/code.md +55 -97
  16. package/reference/sdlc/code.playbook/code.fsm.d.ts +229 -94
  17. package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +26 -44
  18. package/reference/sdlc/code.playbook/code.fsm.introspect.js +61 -66
  19. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +100 -149
  20. package/reference/sdlc/code.playbook/code.fsm.js +587 -1347
  21. package/reference/sdlc/code.playbook/code.fsm.ts +809 -1650
  22. package/reference/sdlc/code.playbook/code.gears.md +51 -263
  23. package/reference/sdlc/code.playbook/code.playbook.d.ts +8 -47
  24. package/reference/sdlc/code.playbook/code.playbook.js +69 -639
  25. package/reference/sdlc/code.playbook/code.playbook.ts +90 -850
  26. package/reference/sdlc/code.playbook/code.registry.d.ts +9 -25
  27. package/reference/sdlc/code.playbook/code.registry.js +20 -78
  28. package/reference/sdlc/code.playbook/code.registry.ts +58 -122
  29. package/reference/sdlc/code.playbook/playbook-captain.d.ts +2 -0
  30. package/reference/sdlc/code.playbook/playbook-captain.js +1877 -251
  31. package/reference/sdlc/code.playbook/playbook-captain.ts +2385 -352
  32. package/reference/sdlc/code.playbook/playbook.config.template.yaml +33 -22
  33. package/reference/sdlc/decide.md +54 -0
  34. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +261 -0
  35. package/reference/sdlc/decide.playbook/decide.fsm.js +894 -0
  36. package/reference/sdlc/decide.playbook/decide.fsm.ts +1152 -0
  37. package/reference/sdlc/decide.playbook/decide.gears.md +88 -0
  38. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +67 -0
  39. package/reference/sdlc/{discuss.playbook/discuss.playbook.js → decide.playbook/decide.playbook.js} +511 -370
  40. package/reference/sdlc/{discuss.playbook/discuss.playbook.ts → decide.playbook/decide.playbook.ts} +616 -451
  41. package/reference/sdlc/decide.playbook/decide.registry.d.ts +41 -0
  42. package/reference/sdlc/decide.playbook/decide.registry.js +60 -0
  43. package/reference/sdlc/decide.playbook/decide.registry.ts +125 -0
  44. package/reference/sdlc/review.md +81 -0
  45. package/reference/sdlc/review.playbook/review.fsm.d.ts +183 -0
  46. package/reference/sdlc/review.playbook/review.fsm.js +524 -0
  47. package/reference/sdlc/review.playbook/review.fsm.ts +652 -0
  48. package/reference/sdlc/review.playbook/review.gears.md +112 -0
  49. package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -0
  50. package/reference/sdlc/review.playbook/review.playbook.js +112 -0
  51. package/reference/sdlc/review.playbook/review.playbook.ts +201 -0
  52. package/reference/sdlc/review.playbook/review.registry.d.ts +43 -0
  53. package/reference/sdlc/review.playbook/review.registry.js +73 -0
  54. package/reference/sdlc/review.playbook/review.registry.ts +138 -0
  55. package/slc/gears2fsm.md +67 -6
  56. package/slc/link.md +339 -25
  57. package/slc/text2gears.md +22 -2
  58. package/src/runtime.d.ts +36 -1
  59. package/src/runtime.ts +59 -0
  60. package/src/xstate-playbook-runtime.d.ts +96 -7
  61. package/src/xstate-playbook-runtime.js +1018 -49
  62. package/src/xstate-playbook-runtime.ts +1283 -59
  63. package/src/xstate-runtime.js +25 -0
  64. package/src/xstate-runtime.ts +51 -0
  65. package/reference/sdlc/discuss.md +0 -93
  66. package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +0 -396
  67. package/reference/sdlc/discuss.playbook/discuss.fsm.js +0 -2067
  68. package/reference/sdlc/discuss.playbook/discuss.fsm.ts +0 -2465
  69. package/reference/sdlc/discuss.playbook/discuss.gears.md +0 -258
  70. package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +0 -113
  71. package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +0 -58
  72. package/reference/sdlc/discuss.playbook/discuss.registry.js +0 -97
  73. package/reference/sdlc/discuss.playbook/discuss.registry.ts +0 -153
@@ -0,0 +1,112 @@
1
+ <!-- SPDX-License-Identifier: Apache-2.0 -->
2
+ <!-- SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai> -->
3
+
4
+ # REVIEW: Commit Review Workflow
5
+
6
+ Players:
7
+
8
+ - Coder
9
+ - Reviewer
10
+
11
+ ### REVIEW-1
12
+
13
+ When the caller starts a review, Captain shall relay the complete caller input to Reviewer with the first-round and shared review instructions:
14
+
15
+ > A new review begins on the latest commit.
16
+ > Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.
17
+ >
18
+ > > <caller-input>
19
+ >
20
+ > Understand the full picture and think systematically about the underlying design.
21
+ > Continue to identify issues or improvements, if any (numbered; no duplication).
22
+ > Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
23
+ > Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
24
+ > For specs, flag stale, missing, over-specified, or under-specified ones.
25
+ > Avoid unnecessary complexity in code or tests, but flag any fundamental design flaw when leaving it would cost more in later patches than fixing it now.
26
+ >
27
+ > Do not re-run tests or builds whose inputs have not changed since any previous reported run.
28
+ > Do not edit files or commit; report findings only.
29
+ >
30
+ > Consult @specs/map.md for context if needed; verify it remains accurate.
31
+ > Consult @specs/meta.md for spec requirements if needed; verify affected specs follow it.
32
+
33
+ Results:
34
+ - `hasFindings`: Reviewer raised one or more unsettled findings. Output shall include `reviewerOutput: <verbatim final text>`.
35
+ - `noFindings`: Reviewer raised no unsettled findings.
36
+
37
+ ### REVIEW-2
38
+
39
+ When Reviewer raises or keeps any finding, Captain shall relay Reviewer's output to Coder with the disposition instruction:
40
+
41
+ > > <reviewer-output>
42
+ >
43
+ > For each review item, accept or reject it.
44
+ > Before deciding, understand the full picture and think systematically about the underlying design.
45
+ > Reject anything that is not essential or is not worth fixing now.
46
+ > If you accept an item, fix its root cause, including any fundamental design flaw - do not patch around it.
47
+ > If you reject an item, give the reasoning and cite code or test output that supports it.
48
+ > Do not re-run tests or builds whose inputs have not changed since any previous reported run.
49
+ >
50
+ > If you accept any item, make minimal changes and commit them as one new commit; never amend the reviewed commit.
51
+ > Follow @specs/packages/git.md.
52
+ > Make the commit message explain concisely what changed and why, including relevant verification.
53
+ > Coder is <coder-llm>; Reviewer is <reviewer-llm>; format model tokens in conventional human form.
54
+ >
55
+ > If you reject every item, change nothing and make no commit.
56
+ > Report every disposition, all relevant run results, and every rebuttal.
57
+
58
+ Results:
59
+ - `committed`: Coder accepted at least one item and made one new commit. Output shall include `coderOutput: <verbatim final text>`.
60
+ - `rejectedAll`: Coder rejected every item and made no commit. Output shall include `coderOutput: <verbatim final text>`.
61
+
62
+ ### REVIEW-3
63
+
64
+ When Coder makes a review-fix commit, Captain shall relay Coder's output to Reviewer with the next-round and shared review instructions:
65
+
66
+ > Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.
67
+ >
68
+ > > <coder-output>
69
+ >
70
+ > Understand the full picture and think systematically about the underlying design.
71
+ > Continue to identify issues or improvements, if any (numbered; no duplication).
72
+ > Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
73
+ > Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
74
+ > For specs, flag stale, missing, over-specified, or under-specified ones.
75
+ > Avoid unnecessary complexity in code or tests, but flag any fundamental design flaw when leaving it would cost more in later patches than fixing it now.
76
+ >
77
+ > Do not re-run tests or builds whose inputs have not changed since any previous reported run.
78
+ > Do not edit files or commit; report findings only.
79
+ >
80
+ > Consult @specs/map.md for context if needed; verify it remains accurate.
81
+ > Consult @specs/meta.md for spec requirements if needed; verify affected specs follow it.
82
+
83
+ Results:
84
+ - `hasFindings`: Reviewer raised one or more unsettled findings. Output shall include `reviewerOutput: <verbatim final text>`.
85
+ - `noFindings`: Reviewer raised no unsettled findings.
86
+
87
+ ### REVIEW-4
88
+
89
+ When Coder rejects every finding and makes no commit, Captain shall relay Coder's output to Reviewer with the rebuttal and shared review instructions:
90
+
91
+ > No new commit was made because Coder rejected every finding.
92
+ > For any rebuttal, accept or challenge it.
93
+ > State which findings, if any, remain.
94
+ >
95
+ > > <coder-output>
96
+ >
97
+ > Understand the full picture and think systematically about the underlying design.
98
+ > Continue to identify issues or improvements, if any (numbered; no duplication).
99
+ > Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
100
+ > Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
101
+ > For specs, flag stale, missing, over-specified, or under-specified ones.
102
+ > Avoid unnecessary complexity in code or tests, but flag any fundamental design flaw when leaving it would cost more in later patches than fixing it now.
103
+ >
104
+ > Do not re-run tests or builds whose inputs have not changed since any previous reported run.
105
+ > Do not edit files or commit; report findings only.
106
+ >
107
+ > Consult @specs/map.md for context if needed; verify it remains accurate.
108
+ > Consult @specs/meta.md for spec requirements if needed; verify affected specs follow it.
109
+
110
+ Results:
111
+ - `hasFindings`: Reviewer kept one or more unsettled findings. Output shall include `reviewerOutput: <verbatim final text>`.
112
+ - `noFindings`: Reviewer accepted the rebuttals and no unsettled findings remain.
@@ -0,0 +1,12 @@
1
+ import { type PlayerInput, type ReviewInput } from './review.fsm.js';
2
+ 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
+ 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
+ export type ReviewPlaybookOptions = ReviewInput;
5
+ /** Keep every line of a relayed runtime value inside its authored quote. */
6
+ declare function composePlayerPrompt(input: PlayerInput): string;
7
+ export declare const _internal: {
8
+ composePlayerPrompt: typeof composePlayerPrompt;
9
+ VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
10
+ };
11
+ declare const createPlaybookRuntime: PlaybookRuntimeFactory<ReviewPlaybookOptions>;
12
+ export default createPlaybookRuntime;
@@ -0,0 +1,112 @@
1
+ // SPDX-License-Identifier: Apache-2.0
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
+ // Player bind: Coder→coder, Reviewer→reviewer (default lowercased binding)
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
11
+ // Compat: artifact schema 1 / runtime ABI 1
12
+ import { RUNTIME_ABI, createXStatePlaybookRuntime, snapshotJsonValue, } from '@sublang/playbook/xstate-runtime';
13
+ import { reviewMachine, } from './review.fsm.js';
14
+ const OPTION_KEYS = new Set(['coderLlm', 'reviewerLlm']);
15
+ const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
16
+ 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
+ const VERBATIM_PAYLOAD_FIELDS = new Set([
18
+ 'reviewerOutput',
19
+ 'coderOutput',
20
+ ]);
21
+ function snapshotReviewOptions(value) {
22
+ const captured = snapshotJsonValue(value, 'REVIEW runtime options');
23
+ if (captured === null ||
24
+ typeof captured !== 'object' ||
25
+ Array.isArray(captured)) {
26
+ throw new TypeError('REVIEW runtime options must be an object');
27
+ }
28
+ const record = captured;
29
+ 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
+ }
39
+ }
40
+ return captured;
41
+ }
42
+ function placeholderField(token) {
43
+ if (token === '#')
44
+ return 'irNumber';
45
+ return token.replace(/-([A-Za-z0-9_$])/g, (_match, next) => next.toUpperCase());
46
+ }
47
+ function quotedContinuation(value) {
48
+ return value.replaceAll('\n', '\n> ');
49
+ }
50
+ /** Keep every line of a relayed runtime value inside its authored quote. */
51
+ function composePlayerPrompt(input) {
52
+ const fields = input;
53
+ const body = input.prompt.replace(PLACEHOLDER, (match, token, offset, source) => {
54
+ const value = fields[placeholderField(token)];
55
+ if (typeof value !== 'string')
56
+ return match;
57
+ const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
58
+ return source.slice(lineStart, offset) === '> '
59
+ ? quotedContinuation(value)
60
+ : value;
61
+ });
62
+ if (input.pendingBossQuestion === undefined ||
63
+ input.bossReply === undefined) {
64
+ return body;
65
+ }
66
+ return [
67
+ CONTINUATION_PREAMBLE,
68
+ `Boss question:\n${input.pendingBossQuestion.question}`,
69
+ `Boss reply:\n${input.bossReply}`,
70
+ body,
71
+ ].join('\n\n');
72
+ }
73
+ export const _internal = {
74
+ composePlayerPrompt,
75
+ VERBATIM_PAYLOAD_FIELDS,
76
+ };
77
+ const runtimeSpec = {
78
+ label: 'REVIEW',
79
+ compat: { artifactSchema: 1, runtimeAbi: RUNTIME_ABI },
80
+ snapshotOptions: snapshotReviewOptions,
81
+ entryEvent: { type: 'START_REVIEW', textField: 'callerInput' },
82
+ playerStates: {
83
+ reviewInitial: {
84
+ player: 'Reviewer',
85
+ label: 'REVIEW-1: Reviewer examines the latest commit against the caller input.',
86
+ },
87
+ addressFindings: {
88
+ player: 'Coder',
89
+ label: 'REVIEW-2: Coder accepts or rejects every current review finding.',
90
+ },
91
+ reviewAfterCommit: {
92
+ player: 'Reviewer',
93
+ label: 'REVIEW-3: Reviewer examines the new review-fix commit and repository state.',
94
+ },
95
+ reviewAfterRebuttal: {
96
+ player: 'Reviewer',
97
+ label: 'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
98
+ },
99
+ },
100
+ composePlayerPrompt: (input) => composePlayerPrompt(input),
101
+ verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
102
+ controlContextFields: [],
103
+ transitionEventFields: [
104
+ 'callerInput',
105
+ 'bossIntent',
106
+ 'targetId',
107
+ 'answer',
108
+ 'questionId',
109
+ ],
110
+ };
111
+ const createPlaybookRuntime = createXStatePlaybookRuntime(reviewMachine, runtimeSpec);
112
+ export default createPlaybookRuntime;
@@ -0,0 +1,201 @@
1
+ // SPDX-License-Identifier: Apache-2.0
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
+ // Player bind: Coder→coder, Reviewer→reviewer (default lowercased binding)
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
11
+ // Compat: artifact schema 1 / runtime ABI 1
12
+
13
+ import {
14
+ RUNTIME_ABI,
15
+ createXStatePlaybookRuntime,
16
+ snapshotJsonValue,
17
+ type PlaybookPlayerInput,
18
+ type XStatePlaybookRuntimeSpec,
19
+ } from '@sublang/playbook/xstate-runtime';
20
+ import {
21
+ reviewMachine,
22
+ type PlayerInput,
23
+ type ReviewInput,
24
+ } from './review.fsm.js';
25
+ import type {
26
+ CaptainCallOptions,
27
+ CaptainResult,
28
+ JsonValue,
29
+ NormalizedError,
30
+ PlayerCallOptions,
31
+ PlaybookCallRequest,
32
+ PlaybookCallResult,
33
+ PlaybookCallStart,
34
+ PlaybookControlReceipt,
35
+ PlaybookControlView,
36
+ PlaybookPendingCall,
37
+ PlaybookPorts,
38
+ PlaybookRunResult,
39
+ PlaybookRuntime,
40
+ PlaybookRuntimeFactory,
41
+ PlaybookRuntimeSnapshot,
42
+ PlaybookSession,
43
+ PlaybookState,
44
+ PlaybookStateValue,
45
+ PlaybookTraceEvent,
46
+ PlaybookTraceType,
47
+ PlayerResult,
48
+ PlayerSessionStore,
49
+ } from '@sublang/playbook/runtime';
50
+
51
+ export type {
52
+ CaptainCallOptions,
53
+ CaptainResult,
54
+ JsonValue,
55
+ NormalizedError,
56
+ PlayerCallOptions,
57
+ PlaybookCallRequest,
58
+ PlaybookCallResult,
59
+ PlaybookCallStart,
60
+ PlaybookControlReceipt,
61
+ PlaybookControlView,
62
+ PlaybookPendingCall,
63
+ PlaybookPorts,
64
+ PlaybookRunResult,
65
+ PlaybookRuntime,
66
+ PlaybookRuntimeFactory,
67
+ PlaybookRuntimeSnapshot,
68
+ PlaybookSession,
69
+ PlaybookState,
70
+ PlaybookStateValue,
71
+ PlaybookTraceEvent,
72
+ PlaybookTraceType,
73
+ PlayerResult,
74
+ PlayerSessionStore,
75
+ };
76
+
77
+ export type ReviewPlaybookOptions = ReviewInput;
78
+
79
+ const OPTION_KEYS = new Set(['coderLlm', 'reviewerLlm']);
80
+ const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
81
+ const CONTINUATION_PREAMBLE =
82
+ 'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
83
+
84
+ const VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string> = new Set([
85
+ 'reviewerOutput',
86
+ 'coderOutput',
87
+ ]);
88
+
89
+ function snapshotReviewOptions(value: unknown): ReviewPlaybookOptions {
90
+ const captured = snapshotJsonValue(value, 'REVIEW runtime options');
91
+ if (
92
+ captured === null ||
93
+ typeof captured !== 'object' ||
94
+ Array.isArray(captured)
95
+ ) {
96
+ throw new TypeError('REVIEW runtime options must be an object');
97
+ }
98
+ const record = captured as Readonly<Record<string, JsonValue>>;
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
+ }
111
+ }
112
+ return captured as unknown as ReviewPlaybookOptions;
113
+ }
114
+
115
+ function placeholderField(token: string): string {
116
+ if (token === '#') return 'irNumber';
117
+ return token.replace(/-([A-Za-z0-9_$])/g, (_match, next: string) =>
118
+ next.toUpperCase(),
119
+ );
120
+ }
121
+
122
+ function quotedContinuation(value: string): string {
123
+ return value.replaceAll('\n', '\n> ');
124
+ }
125
+
126
+ /** Keep every line of a relayed runtime value inside its authored quote. */
127
+ function composePlayerPrompt(input: PlayerInput): 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 = fields[placeholderField(token)];
133
+ if (typeof value !== 'string') return match;
134
+ const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
135
+ return source.slice(lineStart, offset) === '> '
136
+ ? quotedContinuation(value)
137
+ : value;
138
+ },
139
+ );
140
+ if (
141
+ input.pendingBossQuestion === undefined ||
142
+ input.bossReply === undefined
143
+ ) {
144
+ return body;
145
+ }
146
+ return [
147
+ CONTINUATION_PREAMBLE,
148
+ `Boss question:\n${input.pendingBossQuestion.question}`,
149
+ `Boss reply:\n${input.bossReply}`,
150
+ body,
151
+ ].join('\n\n');
152
+ }
153
+
154
+ export const _internal = {
155
+ composePlayerPrompt,
156
+ VERBATIM_PAYLOAD_FIELDS,
157
+ };
158
+
159
+ const runtimeSpec: XStatePlaybookRuntimeSpec<ReviewPlaybookOptions> = {
160
+ label: 'REVIEW',
161
+ compat: { artifactSchema: 1, runtimeAbi: RUNTIME_ABI },
162
+ snapshotOptions: snapshotReviewOptions,
163
+ entryEvent: { type: 'START_REVIEW', textField: 'callerInput' },
164
+ playerStates: {
165
+ reviewInitial: {
166
+ player: 'Reviewer',
167
+ label:
168
+ 'REVIEW-1: Reviewer examines the latest commit against the caller input.',
169
+ },
170
+ addressFindings: {
171
+ player: 'Coder',
172
+ label: 'REVIEW-2: Coder accepts or rejects every current review finding.',
173
+ },
174
+ reviewAfterCommit: {
175
+ player: 'Reviewer',
176
+ label:
177
+ 'REVIEW-3: Reviewer examines the new review-fix commit and repository state.',
178
+ },
179
+ reviewAfterRebuttal: {
180
+ player: 'Reviewer',
181
+ label:
182
+ 'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
183
+ },
184
+ },
185
+ composePlayerPrompt: (input: PlaybookPlayerInput) =>
186
+ composePlayerPrompt(input as PlayerInput),
187
+ verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
188
+ controlContextFields: [],
189
+ transitionEventFields: [
190
+ 'callerInput',
191
+ 'bossIntent',
192
+ 'targetId',
193
+ 'answer',
194
+ 'questionId',
195
+ ],
196
+ };
197
+
198
+ const createPlaybookRuntime: PlaybookRuntimeFactory<ReviewPlaybookOptions> =
199
+ createXStatePlaybookRuntime(reviewMachine, runtimeSpec);
200
+
201
+ export default createPlaybookRuntime;
@@ -0,0 +1,43 @@
1
+ import { type PlaybookRuntime, type ReviewPlaybookOptions } from './review.playbook.js';
2
+ export interface PlaybookSummaryPolicy {
3
+ stateCountLabels: Readonly<Record<string, string>>;
4
+ copyPasteGuardNames: readonly string[];
5
+ savedCountsLine(counts: {
6
+ interruptions: number;
7
+ copyPastes: number;
8
+ }, rounds: number): string;
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
+ export type ReviewOptions = Readonly<Record<string, never>>;
20
+ export interface ReviewPlaybookRegistryEntry {
21
+ id: 'review';
22
+ command: 'review';
23
+ intent: string;
24
+ requiredRoleIds: readonly ['coder', 'reviewer'];
25
+ summaryPolicy: PlaybookSummaryPolicy;
26
+ validateOptions(captainOptions: unknown): ReviewOptions;
27
+ createRuntime(options: CreateReviewRuntimeOptions): PlaybookRuntime;
28
+ }
29
+ export declare const reviewStateCountLabels: {
30
+ readonly reviewInitial: "review round";
31
+ readonly reviewAfterCommit: "review round";
32
+ readonly reviewAfterRebuttal: "rebuttal";
33
+ };
34
+ export declare const reviewCopyPasteGuardNames: readonly ["hasFindings", "committed", "rejectedAll"];
35
+ export declare function reviewSavedCountsLine(counts: {
36
+ interruptions: number;
37
+ copyPastes: number;
38
+ }, rounds: number): string;
39
+ export declare const reviewSummaryPolicy: PlaybookSummaryPolicy;
40
+ export declare function validateReviewOptions(optionSlice: unknown): ReviewOptions;
41
+ export declare function createReviewRuntimeOptions({ captainOptions, players, }: CreateReviewRuntimeOptions): ReviewPlaybookOptions;
42
+ export declare const reviewPlaybookRegistryEntry: ReviewPlaybookRegistryEntry;
43
+ export default reviewPlaybookRegistryEntry;
@@ -0,0 +1,73 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ import createPlaybookRuntime from './review.playbook.js';
4
+ export const reviewStateCountLabels = {
5
+ reviewInitial: 'review round',
6
+ reviewAfterCommit: 'review round',
7
+ reviewAfterRebuttal: 'rebuttal',
8
+ };
9
+ export const reviewCopyPasteGuardNames = [
10
+ 'hasFindings',
11
+ 'committed',
12
+ 'rejectedAll',
13
+ ];
14
+ function countNoun(count, singular, plural = `${singular}s`) {
15
+ return `${count} ${count === 1 ? singular : plural}`;
16
+ }
17
+ export function reviewSavedCountsLine(counts, rounds) {
18
+ return [
19
+ 'Saved you',
20
+ countNoun(counts.interruptions, 'interruption'),
21
+ 'and',
22
+ countNoun(counts.copyPastes, 'copy-paste'),
23
+ 'across',
24
+ countNoun(rounds, 'round'),
25
+ 'of reviews/rebuttals.',
26
+ ].join(' ');
27
+ }
28
+ export const reviewSummaryPolicy = {
29
+ stateCountLabels: reviewStateCountLabels,
30
+ copyPasteGuardNames: reviewCopyPasteGuardNames,
31
+ savedCountsLine: reviewSavedCountsLine,
32
+ };
33
+ export function validateReviewOptions(optionSlice) {
34
+ if (optionSlice === undefined)
35
+ return Object.freeze({});
36
+ if (optionSlice === null ||
37
+ typeof optionSlice !== 'object' ||
38
+ Array.isArray(optionSlice)) {
39
+ throw new Error('captain.options.playbooks.review.options must be an object');
40
+ }
41
+ const keys = Object.keys(optionSlice);
42
+ if (keys.length > 0) {
43
+ throw new Error(`Unknown config field captain.options.playbooks.review.options.${keys[0]}`);
44
+ }
45
+ return Object.freeze({});
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
+ export const reviewPlaybookRegistryEntry = {
63
+ id: 'review',
64
+ command: 'review',
65
+ intent: 'review the latest commit until no material correctness or spec findings remain',
66
+ requiredRoleIds: ['coder', 'reviewer'],
67
+ summaryPolicy: reviewSummaryPolicy,
68
+ validateOptions: validateReviewOptions,
69
+ createRuntime(options) {
70
+ return createPlaybookRuntime(createReviewRuntimeOptions(options));
71
+ },
72
+ };
73
+ export default reviewPlaybookRegistryEntry;
@@ -0,0 +1,138 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+
4
+ import createPlaybookRuntime, {
5
+ type PlaybookRuntime,
6
+ type ReviewPlaybookOptions,
7
+ } from './review.playbook.js';
8
+
9
+ export interface PlaybookSummaryPolicy {
10
+ stateCountLabels: Readonly<Record<string, string>>;
11
+ copyPasteGuardNames: readonly string[];
12
+ savedCountsLine(
13
+ counts: { interruptions: number; copyPastes: number },
14
+ rounds: number,
15
+ ): string;
16
+ }
17
+
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
+ export type ReviewOptions = Readonly<Record<string, never>>;
30
+
31
+ export interface ReviewPlaybookRegistryEntry {
32
+ id: 'review';
33
+ command: 'review';
34
+ intent: string;
35
+ requiredRoleIds: readonly ['coder', 'reviewer'];
36
+ summaryPolicy: PlaybookSummaryPolicy;
37
+ validateOptions(captainOptions: unknown): ReviewOptions;
38
+ createRuntime(options: CreateReviewRuntimeOptions): PlaybookRuntime;
39
+ }
40
+
41
+ export const reviewStateCountLabels = {
42
+ reviewInitial: 'review round',
43
+ reviewAfterCommit: 'review round',
44
+ reviewAfterRebuttal: 'rebuttal',
45
+ } as const;
46
+
47
+ export const reviewCopyPasteGuardNames = [
48
+ 'hasFindings',
49
+ 'committed',
50
+ 'rejectedAll',
51
+ ] as const;
52
+
53
+ function countNoun(
54
+ count: number,
55
+ singular: string,
56
+ plural = `${singular}s`,
57
+ ): string {
58
+ return `${count} ${count === 1 ? singular : plural}`;
59
+ }
60
+
61
+ export function reviewSavedCountsLine(
62
+ counts: { interruptions: number; copyPastes: number },
63
+ rounds: number,
64
+ ): string {
65
+ return [
66
+ 'Saved you',
67
+ countNoun(counts.interruptions, 'interruption'),
68
+ 'and',
69
+ countNoun(counts.copyPastes, 'copy-paste'),
70
+ 'across',
71
+ countNoun(rounds, 'round'),
72
+ 'of reviews/rebuttals.',
73
+ ].join(' ');
74
+ }
75
+
76
+ export const reviewSummaryPolicy: PlaybookSummaryPolicy = {
77
+ stateCountLabels: reviewStateCountLabels,
78
+ copyPasteGuardNames: reviewCopyPasteGuardNames,
79
+ savedCountsLine: reviewSavedCountsLine,
80
+ };
81
+
82
+ export function validateReviewOptions(optionSlice: unknown): ReviewOptions {
83
+ if (optionSlice === undefined) return Object.freeze({});
84
+ if (
85
+ optionSlice === null ||
86
+ typeof optionSlice !== 'object' ||
87
+ Array.isArray(optionSlice)
88
+ ) {
89
+ throw new Error(
90
+ 'captain.options.playbooks.review.options must be an object',
91
+ );
92
+ }
93
+ const keys = Object.keys(optionSlice);
94
+ if (keys.length > 0) {
95
+ throw new Error(
96
+ `Unknown config field captain.options.playbooks.review.options.${keys[0]}`,
97
+ );
98
+ }
99
+ return Object.freeze({});
100
+ }
101
+
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
+ export const reviewPlaybookRegistryEntry: ReviewPlaybookRegistryEntry = {
126
+ id: 'review',
127
+ command: 'review',
128
+ intent:
129
+ 'review the latest commit until no material correctness or spec findings remain',
130
+ requiredRoleIds: ['coder', 'reviewer'],
131
+ summaryPolicy: reviewSummaryPolicy,
132
+ validateOptions: validateReviewOptions,
133
+ createRuntime(options) {
134
+ return createPlaybookRuntime(createReviewRuntimeOptions(options));
135
+ },
136
+ };
137
+
138
+ export default reviewPlaybookRegistryEntry;