@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.
- package/README.md +20 -7
- package/docs/cli.md +88 -43
- package/docs/configuration.md +221 -119
- package/docs/embedding.md +78 -27
- package/package.json +4 -3
- package/reference/sdlc/captain.playbook/captain.playbook.js +16 -5
- package/reference/sdlc/captain.playbook/captain.playbook.ts +20 -6
- package/reference/sdlc/code.md +1 -1
- package/reference/sdlc/code.playbook/bin/interactive-session.js +816 -0
- package/reference/sdlc/code.playbook/bin/launch-config.js +1078 -116
- package/reference/sdlc/code.playbook/bin/playbook.js +489 -34
- package/reference/sdlc/code.playbook/bin/run.js +283 -298
- package/reference/sdlc/code.playbook/bin/session-store.js +818 -26
- package/reference/sdlc/code.playbook/code.fsm.d.ts +9 -6
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +2 -2
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +2 -2
- package/reference/sdlc/code.playbook/code.fsm.js +18 -15
- package/reference/sdlc/code.playbook/code.fsm.ts +21 -21
- package/reference/sdlc/code.playbook/code.gears.md +1 -1
- package/reference/sdlc/code.playbook/code.playbook.d.ts +2 -1
- package/reference/sdlc/code.playbook/code.playbook.js +25 -15
- package/reference/sdlc/code.playbook/code.playbook.ts +34 -17
- package/reference/sdlc/code.playbook/code.registry.d.ts +5 -13
- package/reference/sdlc/code.playbook/code.registry.js +3 -10
- package/reference/sdlc/code.playbook/code.registry.ts +7 -32
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +39 -14
- package/reference/sdlc/code.playbook/playbook-captain.js +1014 -299
- package/reference/sdlc/code.playbook/playbook-captain.ts +1450 -406
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +41 -49
- package/reference/sdlc/decide.md +4 -4
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +10 -10
- package/reference/sdlc/decide.playbook/decide.fsm.js +21 -14
- package/reference/sdlc/decide.playbook/decide.fsm.ts +27 -23
- package/reference/sdlc/decide.playbook/decide.gears.md +3 -5
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +11 -13
- package/reference/sdlc/decide.playbook/decide.playbook.js +465 -246
- package/reference/sdlc/decide.playbook/decide.playbook.ts +623 -283
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +5 -13
- package/reference/sdlc/decide.playbook/decide.registry.js +3 -9
- package/reference/sdlc/decide.playbook/decide.registry.ts +7 -31
- package/reference/sdlc/review.md +4 -5
- package/reference/sdlc/review.playbook/review.fsm.d.ts +9 -11
- package/reference/sdlc/review.playbook/review.fsm.js +30 -24
- package/reference/sdlc/review.playbook/review.fsm.ts +39 -35
- package/reference/sdlc/review.playbook/review.gears.md +6 -5
- package/reference/sdlc/review.playbook/review.playbook.d.ts +2 -1
- package/reference/sdlc/review.playbook/review.playbook.js +29 -23
- package/reference/sdlc/review.playbook/review.playbook.ts +38 -28
- package/reference/sdlc/review.playbook/review.registry.d.ts +5 -13
- package/reference/sdlc/review.playbook/review.registry.js +3 -16
- package/reference/sdlc/review.playbook/review.registry.ts +7 -38
- package/slc/gears2fsm.md +45 -24
- package/slc/link.md +297 -135
- package/slc/text2gears.md +19 -18
- package/src/runtime.d.ts +21 -16
- package/src/runtime.ts +20 -23
- package/src/xstate-playbook-runtime.d.ts +34 -20
- package/src/xstate-playbook-runtime.js +973 -400
- package/src/xstate-playbook-runtime.ts +1203 -457
- package/src/xstate-runtime.d.ts +17 -7
- package/src/xstate-runtime.js +198 -81
- package/src/xstate-runtime.ts +339 -112
|
@@ -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
|
-
//
|
|
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
|
|
12
|
-
import {
|
|
11
|
+
// Compat: artifact schema 2 / runtime ABI 1
|
|
12
|
+
import { 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
|
-
|
|
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 =
|
|
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,39 @@ export const _internal = {
|
|
|
76
71
|
};
|
|
77
72
|
const runtimeSpec = {
|
|
78
73
|
label: 'REVIEW',
|
|
79
|
-
|
|
74
|
+
// DR-022 / slc/link.md: the declaration carries the value current at link
|
|
75
|
+
// time — a literal, never the loading engine's RUNTIME_ABI self-report,
|
|
76
|
+
// which would follow whatever engine loads the module and make the
|
|
77
|
+
// factory's skew check compare that engine with itself.
|
|
78
|
+
compat: { artifactSchema: 2, runtimeAbi: 1 },
|
|
80
79
|
snapshotOptions: snapshotReviewOptions,
|
|
81
|
-
entryEvent: {
|
|
82
|
-
|
|
80
|
+
entryEvent: {
|
|
81
|
+
type: 'START_REVIEW',
|
|
82
|
+
textField: 'callerInput',
|
|
83
|
+
// `copyStartInput` copies the entry text here, so the failure-state
|
|
84
|
+
// retry reads it back from the persisted machine snapshot and survives
|
|
85
|
+
// a continued session (DR-034).
|
|
86
|
+
contextField: 'callerInput',
|
|
87
|
+
},
|
|
88
|
+
roleStates: {
|
|
83
89
|
reviewInitial: {
|
|
84
|
-
|
|
90
|
+
role: 'reviewer',
|
|
85
91
|
label: 'REVIEW-1: Reviewer examines the latest commit against the caller input.',
|
|
86
92
|
},
|
|
87
93
|
addressFindings: {
|
|
88
|
-
|
|
94
|
+
role: 'coder',
|
|
89
95
|
label: 'REVIEW-2: Coder accepts or rejects every current review finding.',
|
|
90
96
|
},
|
|
91
97
|
reviewAfterCommit: {
|
|
92
|
-
|
|
98
|
+
role: 'reviewer',
|
|
93
99
|
label: 'REVIEW-3: Reviewer examines the new review-fix commit and repository state.',
|
|
94
100
|
},
|
|
95
101
|
reviewAfterRebuttal: {
|
|
96
|
-
|
|
102
|
+
role: 'reviewer',
|
|
97
103
|
label: 'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
|
|
98
104
|
},
|
|
99
105
|
},
|
|
100
|
-
composePlayerPrompt: (input) => composePlayerPrompt(input),
|
|
106
|
+
composePlayerPrompt: (input, promptIdentity) => composePlayerPrompt(input, promptIdentity),
|
|
101
107
|
verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
|
|
102
108
|
controlContextFields: [],
|
|
103
109
|
transitionEventFields: [
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Generated by slc/link.md (FSM-to-Runtime linker).
|
|
5
5
|
// Source FSM: ./review.fsm.ts
|
|
6
|
-
//
|
|
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
|
|
11
|
+
// Compat: artifact schema 2 / runtime ABI 1
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
-
RUNTIME_ABI,
|
|
15
14
|
createXStatePlaybookRuntime,
|
|
16
15
|
snapshotJsonValue,
|
|
17
16
|
type PlaybookPlayerInput,
|
|
17
|
+
type XStatePromptIdentity,
|
|
18
18
|
type XStatePlaybookRuntimeSpec,
|
|
19
19
|
} from '@sublang/playbook/xstate-runtime';
|
|
20
20
|
import {
|
|
@@ -76,7 +76,6 @@ export type {
|
|
|
76
76
|
|
|
77
77
|
export type ReviewPlaybookOptions = ReviewInput;
|
|
78
78
|
|
|
79
|
-
const OPTION_KEYS = new Set(['coderLlm', 'reviewerLlm']);
|
|
80
79
|
const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
|
|
81
80
|
const CONTINUATION_PREAMBLE =
|
|
82
81
|
'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
|
|
@@ -97,17 +96,7 @@ function snapshotReviewOptions(value: unknown): ReviewPlaybookOptions {
|
|
|
97
96
|
}
|
|
98
97
|
const record = captured as Readonly<Record<string, JsonValue>>;
|
|
99
98
|
for (const key of Object.keys(record)) {
|
|
100
|
-
|
|
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
|
-
}
|
|
99
|
+
throw new TypeError(`REVIEW runtime options.${key} is not declared`);
|
|
111
100
|
}
|
|
112
101
|
return captured as unknown as ReviewPlaybookOptions;
|
|
113
102
|
}
|
|
@@ -124,12 +113,20 @@ function quotedContinuation(value: string): string {
|
|
|
124
113
|
}
|
|
125
114
|
|
|
126
115
|
/** Keep every line of a relayed runtime value inside its authored quote. */
|
|
127
|
-
function composePlayerPrompt(
|
|
116
|
+
function composePlayerPrompt(
|
|
117
|
+
input: PlayerInput,
|
|
118
|
+
promptIdentity: XStatePromptIdentity,
|
|
119
|
+
): string {
|
|
128
120
|
const fields = input as unknown as Record<string, unknown>;
|
|
129
121
|
const body = input.prompt.replace(
|
|
130
122
|
PLACEHOLDER,
|
|
131
123
|
(match: string, token: string, offset: number, source: string) => {
|
|
132
|
-
const value =
|
|
124
|
+
const value =
|
|
125
|
+
token === 'coder-llm'
|
|
126
|
+
? promptIdentity('coder')
|
|
127
|
+
: token === 'reviewer-llm'
|
|
128
|
+
? promptIdentity('reviewer')
|
|
129
|
+
: fields[placeholderField(token)];
|
|
133
130
|
if (typeof value !== 'string') return match;
|
|
134
131
|
const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
|
|
135
132
|
return source.slice(lineStart, offset) === '> '
|
|
@@ -156,34 +153,47 @@ export const _internal = {
|
|
|
156
153
|
VERBATIM_PAYLOAD_FIELDS,
|
|
157
154
|
};
|
|
158
155
|
|
|
159
|
-
const runtimeSpec
|
|
156
|
+
const runtimeSpec = {
|
|
160
157
|
label: 'REVIEW',
|
|
161
|
-
|
|
158
|
+
// DR-022 / slc/link.md: the declaration carries the value current at link
|
|
159
|
+
// time — a literal, never the loading engine's RUNTIME_ABI self-report,
|
|
160
|
+
// which would follow whatever engine loads the module and make the
|
|
161
|
+
// factory's skew check compare that engine with itself.
|
|
162
|
+
compat: { artifactSchema: 2, runtimeAbi: 1 },
|
|
162
163
|
snapshotOptions: snapshotReviewOptions,
|
|
163
|
-
entryEvent: {
|
|
164
|
-
|
|
164
|
+
entryEvent: {
|
|
165
|
+
type: 'START_REVIEW',
|
|
166
|
+
textField: 'callerInput',
|
|
167
|
+
// `copyStartInput` copies the entry text here, so the failure-state
|
|
168
|
+
// retry reads it back from the persisted machine snapshot and survives
|
|
169
|
+
// a continued session (DR-034).
|
|
170
|
+
contextField: 'callerInput',
|
|
171
|
+
},
|
|
172
|
+
roleStates: {
|
|
165
173
|
reviewInitial: {
|
|
166
|
-
|
|
174
|
+
role: 'reviewer',
|
|
167
175
|
label:
|
|
168
176
|
'REVIEW-1: Reviewer examines the latest commit against the caller input.',
|
|
169
177
|
},
|
|
170
178
|
addressFindings: {
|
|
171
|
-
|
|
179
|
+
role: 'coder',
|
|
172
180
|
label: 'REVIEW-2: Coder accepts or rejects every current review finding.',
|
|
173
181
|
},
|
|
174
182
|
reviewAfterCommit: {
|
|
175
|
-
|
|
183
|
+
role: 'reviewer',
|
|
176
184
|
label:
|
|
177
185
|
'REVIEW-3: Reviewer examines the new review-fix commit and repository state.',
|
|
178
186
|
},
|
|
179
187
|
reviewAfterRebuttal: {
|
|
180
|
-
|
|
188
|
+
role: 'reviewer',
|
|
181
189
|
label:
|
|
182
190
|
'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
|
|
183
191
|
},
|
|
184
192
|
},
|
|
185
|
-
composePlayerPrompt: (
|
|
186
|
-
|
|
193
|
+
composePlayerPrompt: (
|
|
194
|
+
input: PlaybookPlayerInput,
|
|
195
|
+
promptIdentity: XStatePromptIdentity,
|
|
196
|
+
) => composePlayerPrompt(input as PlayerInput, promptIdentity),
|
|
187
197
|
verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
|
|
188
198
|
controlContextFields: [],
|
|
189
199
|
transitionEventFields: [
|
|
@@ -193,7 +203,7 @@ const runtimeSpec: XStatePlaybookRuntimeSpec<ReviewPlaybookOptions> = {
|
|
|
193
203
|
'answer',
|
|
194
204
|
'questionId',
|
|
195
205
|
],
|
|
196
|
-
}
|
|
206
|
+
} satisfies XStatePlaybookRuntimeSpec<ReviewPlaybookOptions>;
|
|
197
207
|
|
|
198
208
|
const createPlaybookRuntime: PlaybookRuntimeFactory<ReviewPlaybookOptions> =
|
|
199
209
|
createXStatePlaybookRuntime(reviewMachine, runtimeSpec);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type PlaybookRuntime
|
|
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(
|
|
27
|
-
createRuntime(options:
|
|
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(
|
|
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(
|
|
38
|
-
createRuntime(options:
|
|
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(
|
|
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
|
|
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
|
-
- `
|
|
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,25 @@ 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,
|
|
248
|
-
|
|
247
|
+
- JSON-safe `meta: { playbook: { stateId, description, role? } }` naming the
|
|
248
|
+
state's public playbook identity per the identity rule below and repeating
|
|
249
|
+
its description so linked runtimes can discover active public
|
|
249
250
|
identities through `snapshot.getMeta()` without private XState nodes. A
|
|
250
|
-
delegated-
|
|
251
|
-
`meta.playbook.
|
|
251
|
+
delegated-role state shall also carry the canonical lowercase source role id in
|
|
252
|
+
`meta.playbook.role`; every other state shall omit `role`;
|
|
252
253
|
- if it invokes the direct `captain` actor: `invoke.input` carrying
|
|
253
254
|
`sourceItem`, `prompt`, and `result` (per [Setup](#setup));
|
|
254
255
|
- if it invokes the delegated `player` actor: `invoke.input` additionally
|
|
255
|
-
carrying the same source-derived `
|
|
256
|
+
carrying the same source-derived `role` as `meta.playbook.role`;
|
|
256
257
|
- if it invokes the `script` actor: `invoke.input` carrying `stateId`,
|
|
257
258
|
`sourceItem`, `command`, and `result` (per [Setup](#setup)) — no `prompt`
|
|
258
|
-
and no `
|
|
259
|
+
and no `role`.
|
|
259
260
|
|
|
260
261
|
The source item ID shall live in `invoke.input.sourceItem`, not in a comment — this keeps the GEARS-to-state mapping machine-readable.
|
|
261
|
-
|
|
262
|
-
|
|
262
|
+
|
|
263
|
+
Outside a parallel group's regions, a state's `meta.playbook.stateId` shall equal its state key — the one identity a factory-backed linked runtime indexes by.
|
|
264
|
+
A delegated state's `invoke.input.role` shall match the canonical lowercase id of its source item's named role.
|
|
265
|
+
A direct Captain state shall not invent a `Captain` role binding.
|
|
263
266
|
|
|
264
267
|
Every invoking working leaf — sequential or parallel, whatever its actor
|
|
265
268
|
kind — shall carry the tag `playbook.busy`: the shared quiescence helper
|
|
@@ -281,7 +284,7 @@ invoke: {
|
|
|
281
284
|
src: 'player',
|
|
282
285
|
input: ({ context }): PlayerInput => ({
|
|
283
286
|
stateId: '<stable-state-id>',
|
|
284
|
-
|
|
287
|
+
role: 'reviewer',
|
|
285
288
|
sourceItem: '<ITEM-A>',
|
|
286
289
|
prompt: [
|
|
287
290
|
'Flag any issues or improvements (numbered; no duplication).',
|
|
@@ -299,17 +302,17 @@ invoke: {
|
|
|
299
302
|
|
|
300
303
|
For an item in which Captain acts directly, the corresponding invocation uses
|
|
301
304
|
`src: 'captain'` and a `CaptainInput` with the same static mapping fields but no
|
|
302
|
-
`
|
|
305
|
+
`role` field.
|
|
303
306
|
|
|
304
307
|
## Mapping
|
|
305
308
|
|
|
306
309
|
Each Source spec item shall map to exactly one state in Target.
|
|
307
310
|
A state's `invoke.input.sourceItem` shall be that item's ID, and `invoke.input.prompt` shall carry the item's prompt verbatim.
|
|
308
311
|
|
|
309
|
-
An item written as direct Captain work shall map to exactly one `captain`
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
312
|
+
An item written as direct Captain work shall map to exactly one `captain` invocation.
|
|
313
|
+
An item that prompts or relays to a named role shall map to exactly one `player` invocation.
|
|
314
|
+
A nested-call item shall map to exactly one `playbook` invocation.
|
|
315
|
+
A script item (`Captain shall run:`) shall map to
|
|
313
316
|
exactly one `script` invocation whose `input.command` carries the blockquote
|
|
314
317
|
verbatim and whose `result` preserves the item's two guards in declared order.
|
|
315
318
|
The compiler shall not infer one actor kind from a
|
|
@@ -333,10 +336,14 @@ Each member shall be a delegated-player item; a direct-Captain or nested-call
|
|
|
333
336
|
member is malformed because those actor kinds share one Captain control lane or one
|
|
334
337
|
pending-child slot. Each region shall contain a delegated-player working leaf
|
|
335
338
|
and a local final state; the working leaf retains the item's stable state id,
|
|
336
|
-
`sourceItem`,
|
|
339
|
+
`sourceItem`, role, prompt, and result contract.
|
|
340
|
+
The members' canonical role ids shall be pairwise distinct; a repeated role in one group is malformed.
|
|
337
341
|
The parallel parent shall use `onDone` as the join, which XState takes only
|
|
338
342
|
after every region reaches final.
|
|
339
343
|
|
|
344
|
+
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.
|
|
345
|
+
An artifact with no parallel group shall export an empty array.
|
|
346
|
+
|
|
340
347
|
Each branch shall assign only its own staged result.
|
|
341
348
|
The join shall promote all staged results atomically before later work begins,
|
|
342
349
|
so branch completion order cannot change downstream inputs.
|
|
@@ -527,8 +534,8 @@ the number of sequential child calls without an arbitrary runtime call limit.
|
|
|
527
534
|
Prompts shall pass only the **specific extracted fields** the player needs.
|
|
528
535
|
The compiler shall not dump `JSON.stringify(lastResult)` or any opaque blob: it leaks internal `guard` strings, wastes tokens, and confuses the LLM.
|
|
529
536
|
|
|
530
|
-
Player bindings and
|
|
531
|
-
The artifact shall not bake
|
|
537
|
+
Player bindings and prompt identities shall enter only through `PlaybookSession.roleBindings` at runtime call, prompt, and trace boundaries.
|
|
538
|
+
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
539
|
Host-owned configuration such as an enabled-playbook catalog shall remain
|
|
533
540
|
immutable machine input/context for the session. Boss events and actor outputs
|
|
534
541
|
shall not carry, replace, append to, or otherwise overwrite that catalog.
|
|
@@ -585,7 +592,7 @@ Phases may set typed routing fields so terminal outcomes return to the originati
|
|
|
585
592
|
|
|
586
593
|
## Boss control
|
|
587
594
|
|
|
588
|
-
[Boss](text2gears.md#
|
|
595
|
+
[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
596
|
|
|
590
597
|
### Boss interrupts
|
|
591
598
|
|
|
@@ -667,12 +674,12 @@ the colon as the JSON field name.
|
|
|
667
674
|
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
675
|
|
|
669
676
|
The question record shall be
|
|
670
|
-
`{ questionId, resumeStateId, sourceItem,
|
|
677
|
+
`{ questionId, resumeStateId, sourceItem, asker, question }`, where `asker` is exactly `{ kind: 'captain' }` or `{ kind: 'role', roleId }`.
|
|
671
678
|
`questionId` and `resumeStateId` shall both equal the stable working-leaf
|
|
672
679
|
`stateId`.
|
|
673
|
-
`questionId`, `resumeStateId`, and `sourceItem` shall come from the suspended
|
|
674
|
-
|
|
675
|
-
|
|
680
|
+
`questionId`, `resumeStateId`, and `sourceItem` shall come from the suspended working leaf's stable invocation metadata.
|
|
681
|
+
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.
|
|
682
|
+
Only
|
|
676
683
|
`question` shall come from adjudicated actor output.
|
|
677
684
|
|
|
678
685
|
A machine with at most one active Captain or player task may use the scalar
|
|
@@ -744,6 +751,20 @@ an unhandled runtime error.
|
|
|
744
751
|
Every machine shall declare at least one `type: 'final'` state (typically `done`) reachable on completion.
|
|
745
752
|
A never-terminating machine is a defect: the runner has no completion signal.
|
|
746
753
|
|
|
754
|
+
At least one is a floor, not a ceiling. A final state's `description` is the
|
|
755
|
+
machine's published terminal meaning: a host that cannot read the machine's
|
|
756
|
+
output quotes that description to report what the run did. It shall therefore
|
|
757
|
+
be true of every arm that enters the state and of no other terminal outcome.
|
|
758
|
+
Where Source declares more than one terminal outcome — an approval that
|
|
759
|
+
completes the workflow and a failure the workflow reports to its caller
|
|
760
|
+
instead of parking — each outcome shall get its own `type: 'final'` state
|
|
761
|
+
whose description names it. Routing an approval arm and a failure, abort, or
|
|
762
|
+
invalid-result arm into one final state is a defect of the same kind as a
|
|
763
|
+
wrong result field, because the quoting host cannot detect the difference.
|
|
764
|
+
This constrains only published meaning: the declared machine `output` still
|
|
765
|
+
derives its status and fields from typed context, so a caller that does read
|
|
766
|
+
the output is unaffected.
|
|
767
|
+
|
|
747
768
|
Where Source declares a JSON-safe terminal result, the setup types shall
|
|
748
769
|
declare that output and the root machine shall derive it from typed context
|
|
749
770
|
through XState's machine `output` function. A final-state transition alone does
|