@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,4 +1,4 @@
|
|
|
1
|
-
import { type PlaybookRuntime
|
|
1
|
+
import { type PlaybookRuntime } from './decide.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 CreateDecideRuntimeOptions {
|
|
16
|
-
captainOptions: unknown;
|
|
17
|
-
players: readonly RegistryPlayer[];
|
|
18
|
-
}
|
|
19
10
|
export type DecideOptions = Readonly<Record<string, never>>;
|
|
20
11
|
export interface DecidePlaybookRegistryEntry {
|
|
21
12
|
id: 'decide';
|
|
22
13
|
command: 'decide';
|
|
23
14
|
intent: string;
|
|
15
|
+
artifactSchema: 2;
|
|
24
16
|
requiredRoleIds: readonly ['coder', 'reviewer'];
|
|
17
|
+
concurrentRoleSets: readonly [readonly ['coder', 'reviewer']];
|
|
25
18
|
summaryPolicy: PlaybookSummaryPolicy;
|
|
26
|
-
validateOptions(
|
|
27
|
-
createRuntime(options:
|
|
19
|
+
validateOptions(optionSlice: unknown): DecideOptions;
|
|
20
|
+
createRuntime(options: DecideOptions): PlaybookRuntime;
|
|
28
21
|
}
|
|
29
22
|
export declare const decideStateCountLabels: {
|
|
30
23
|
readonly independentProposals: "proposal round";
|
|
@@ -36,6 +29,5 @@ export declare function decideSavedCountsLine(counts: {
|
|
|
36
29
|
}, rounds: number): string;
|
|
37
30
|
export declare const decideSummaryPolicy: PlaybookSummaryPolicy;
|
|
38
31
|
export declare function validateDecideOptions(optionSlice: unknown): DecideOptions;
|
|
39
|
-
export declare function createDecideRuntimeOptions({ captainOptions, players, }: CreateDecideRuntimeOptions): PlaybookRuntimeOptions;
|
|
40
32
|
export declare const decidePlaybookRegistryEntry: DecidePlaybookRegistryEntry;
|
|
41
33
|
export default decidePlaybookRegistryEntry;
|
|
@@ -38,23 +38,17 @@ export function validateDecideOptions(optionSlice) {
|
|
|
38
38
|
}
|
|
39
39
|
return Object.freeze({});
|
|
40
40
|
}
|
|
41
|
-
function playerIdentity(players, id) {
|
|
42
|
-
const player = players.find((entry) => entry.id === id);
|
|
43
|
-
return player?.model ?? player?.adapter ?? id;
|
|
44
|
-
}
|
|
45
|
-
export function createDecideRuntimeOptions({ captainOptions, players, }) {
|
|
46
|
-
validateDecideOptions(captainOptions);
|
|
47
|
-
return { coderLlm: playerIdentity(players, 'coder') };
|
|
48
|
-
}
|
|
49
41
|
export const decidePlaybookRegistryEntry = {
|
|
50
42
|
id: 'decide',
|
|
51
43
|
command: 'decide',
|
|
52
44
|
intent: 'turn independent Coder and Reviewer proposals into an approved spec-design commit',
|
|
45
|
+
artifactSchema: 2,
|
|
53
46
|
requiredRoleIds: ['coder', 'reviewer'],
|
|
47
|
+
concurrentRoleSets: [['coder', 'reviewer']],
|
|
54
48
|
summaryPolicy: decideSummaryPolicy,
|
|
55
49
|
validateOptions: validateDecideOptions,
|
|
56
50
|
createRuntime(options) {
|
|
57
|
-
return createPlaybookRuntime(
|
|
51
|
+
return createPlaybookRuntime(options);
|
|
58
52
|
},
|
|
59
53
|
};
|
|
60
54
|
export default decidePlaybookRegistryEntry;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
import createPlaybookRuntime, {
|
|
5
5
|
type PlaybookRuntime,
|
|
6
|
-
type PlaybookRuntimeOptions,
|
|
7
6
|
} from './decide.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 CreateDecideRuntimeOptions {
|
|
25
|
-
captainOptions: unknown;
|
|
26
|
-
players: readonly RegistryPlayer[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
17
|
export type DecideOptions = Readonly<Record<string, never>>;
|
|
30
18
|
|
|
31
19
|
export interface DecidePlaybookRegistryEntry {
|
|
32
20
|
id: 'decide';
|
|
33
21
|
command: 'decide';
|
|
34
22
|
intent: string;
|
|
23
|
+
artifactSchema: 2;
|
|
35
24
|
requiredRoleIds: readonly ['coder', 'reviewer'];
|
|
25
|
+
concurrentRoleSets: readonly [readonly ['coder', 'reviewer']];
|
|
36
26
|
summaryPolicy: PlaybookSummaryPolicy;
|
|
37
|
-
validateOptions(
|
|
38
|
-
createRuntime(options:
|
|
27
|
+
validateOptions(optionSlice: unknown): DecideOptions;
|
|
28
|
+
createRuntime(options: DecideOptions): PlaybookRuntime;
|
|
39
29
|
}
|
|
40
30
|
|
|
41
31
|
export const decideStateCountLabels = {
|
|
@@ -93,32 +83,18 @@ export function validateDecideOptions(optionSlice: unknown): DecideOptions {
|
|
|
93
83
|
return Object.freeze({});
|
|
94
84
|
}
|
|
95
85
|
|
|
96
|
-
function playerIdentity(
|
|
97
|
-
players: readonly RegistryPlayer[],
|
|
98
|
-
id: string,
|
|
99
|
-
): string {
|
|
100
|
-
const player = players.find((entry) => entry.id === id);
|
|
101
|
-
return player?.model ?? player?.adapter ?? id;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function createDecideRuntimeOptions({
|
|
105
|
-
captainOptions,
|
|
106
|
-
players,
|
|
107
|
-
}: CreateDecideRuntimeOptions): PlaybookRuntimeOptions {
|
|
108
|
-
validateDecideOptions(captainOptions);
|
|
109
|
-
return { coderLlm: playerIdentity(players, 'coder') };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
86
|
export const decidePlaybookRegistryEntry: DecidePlaybookRegistryEntry = {
|
|
113
87
|
id: 'decide',
|
|
114
88
|
command: 'decide',
|
|
115
89
|
intent:
|
|
116
90
|
'turn independent Coder and Reviewer proposals into an approved spec-design commit',
|
|
91
|
+
artifactSchema: 2,
|
|
117
92
|
requiredRoleIds: ['coder', 'reviewer'],
|
|
93
|
+
concurrentRoleSets: [['coder', 'reviewer']],
|
|
118
94
|
summaryPolicy: decideSummaryPolicy,
|
|
119
95
|
validateOptions: validateDecideOptions,
|
|
120
96
|
createRuntime(options) {
|
|
121
|
-
return createPlaybookRuntime(
|
|
97
|
+
return createPlaybookRuntime(options);
|
|
122
98
|
},
|
|
123
99
|
};
|
|
124
100
|
|
package/reference/sdlc/review.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Review
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Roles:
|
|
8
8
|
|
|
9
9
|
- Coder
|
|
10
10
|
- Reviewer
|
|
@@ -20,21 +20,19 @@ At the first review round, Captain shall give Reviewer the following instruction
|
|
|
20
20
|
|
|
21
21
|
```markdown
|
|
22
22
|
A new review begins on the latest commit.
|
|
23
|
-
Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.
|
|
23
|
+
Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
After every review-fix commit, Captain shall give Reviewer the following instruction:
|
|
27
27
|
|
|
28
28
|
```markdown
|
|
29
|
-
Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.
|
|
29
|
+
Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
When Coder rejects every finding and makes no commit, Captain shall give Reviewer the following instruction:
|
|
33
33
|
|
|
34
34
|
```markdown
|
|
35
35
|
No new commit was made because Coder rejected every finding.
|
|
36
|
-
For any rebuttal, accept or challenge it.
|
|
37
|
-
State which findings, if any, remain.
|
|
38
36
|
```
|
|
39
37
|
|
|
40
38
|
At the start of *every* review round, Captain shall relay to Reviewer any Coder feedback from the preceding round and any relevant run results, in quotes (`>`) after the instruction.
|
|
@@ -44,6 +42,7 @@ At the start of *every* review round, Captain shall append the following instruc
|
|
|
44
42
|
```markdown
|
|
45
43
|
Understand the full picture and think systematically about the underlying design.
|
|
46
44
|
Continue to identify issues or improvements, if any (numbered; no duplication).
|
|
45
|
+
For any rebuttal, accept or challenge it.
|
|
47
46
|
Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
|
|
48
47
|
Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
|
|
49
48
|
For specs, flag stale, missing, over-specified, or under-specified ones.
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
type
|
|
1
|
+
type Role = 'coder' | 'reviewer';
|
|
2
2
|
type JumpableStateId = 'reviewInitial';
|
|
3
3
|
export type ResumableStateId = 'reviewInitial' | 'addressFindings' | 'reviewAfterCommit' | 'reviewAfterRebuttal';
|
|
4
4
|
export interface PendingBossQuestion {
|
|
5
5
|
questionId: ResumableStateId;
|
|
6
6
|
resumeStateId: ResumableStateId;
|
|
7
7
|
sourceItem: `REVIEW-${1 | 2 | 3 | 4}`;
|
|
8
|
-
|
|
8
|
+
asker: {
|
|
9
|
+
readonly kind: 'role';
|
|
10
|
+
readonly roleId: Role;
|
|
11
|
+
};
|
|
9
12
|
question: string;
|
|
10
13
|
}
|
|
11
14
|
export interface ReviewOutput {
|
|
12
15
|
approvedCommit: 'latest';
|
|
13
16
|
noUnsettledFindings: true;
|
|
14
17
|
}
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
reviewerLlm: string;
|
|
18
|
-
}
|
|
19
|
-
export interface ReviewContext extends ReviewInput {
|
|
18
|
+
export type ReviewInput = Readonly<Record<string, never>>;
|
|
19
|
+
export interface ReviewContext {
|
|
20
20
|
callerInput?: string;
|
|
21
21
|
reviewerOutput?: string;
|
|
22
22
|
coderOutput?: string;
|
|
@@ -39,14 +39,12 @@ export type ReviewEvent = {
|
|
|
39
39
|
export interface PlayerInput {
|
|
40
40
|
stateId: ResumableStateId;
|
|
41
41
|
sourceItem: `REVIEW-${1 | 2 | 3 | 4}`;
|
|
42
|
-
|
|
42
|
+
role: Role;
|
|
43
43
|
prompt: string;
|
|
44
44
|
result: Readonly<Record<string, string>>;
|
|
45
45
|
callerInput?: string;
|
|
46
46
|
reviewerOutput?: string;
|
|
47
47
|
coderOutput?: string;
|
|
48
|
-
coderLlm?: string;
|
|
49
|
-
reviewerLlm?: string;
|
|
50
48
|
pendingBossQuestion?: PendingBossQuestion;
|
|
51
49
|
bossReply?: string;
|
|
52
50
|
}
|
|
@@ -151,7 +149,7 @@ export declare const reviewMachine: import("xstate").StateMachine<ReviewContext,
|
|
|
151
149
|
} | {
|
|
152
150
|
type: "restartInitialReview";
|
|
153
151
|
params: unknown;
|
|
154
|
-
}, never, "done" | "failed" | "awaitBossReply" | "ready" | "reviewInitial" | "addressFindings" | "reviewAfterCommit" | "reviewAfterRebuttal", string,
|
|
152
|
+
}, never, "done" | "failed" | "awaitBossReply" | "ready" | "reviewInitial" | "addressFindings" | "reviewAfterCommit" | "reviewAfterRebuttal", string, Readonly<Record<string, never>>, ReviewOutput, import("xstate").EventObject, import("xstate").MetaObject, {
|
|
155
153
|
id: "review";
|
|
156
154
|
states: {
|
|
157
155
|
readonly ready: {
|
|
@@ -7,6 +7,7 @@ const NEEDS_BOSS_REPLY_DESCRIPTION = "The acting agent's prose surfaces a clarif
|
|
|
7
7
|
const SHARED_REVIEW_INSTRUCTION = [
|
|
8
8
|
'Understand the full picture and think systematically about the underlying design.',
|
|
9
9
|
'Continue to identify issues or improvements, if any (numbered; no duplication).',
|
|
10
|
+
'For any rebuttal, accept or challenge it.',
|
|
10
11
|
'Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.',
|
|
11
12
|
'Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.',
|
|
12
13
|
'For specs, flag stale, missing, over-specified, or under-specified ones.',
|
|
@@ -20,14 +21,14 @@ const SHARED_REVIEW_INSTRUCTION = [
|
|
|
20
21
|
].join('\n');
|
|
21
22
|
const INITIAL_REVIEW_PROMPT = [
|
|
22
23
|
'A new review begins on the latest commit.',
|
|
23
|
-
'Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.',
|
|
24
|
+
'Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.',
|
|
24
25
|
'',
|
|
25
26
|
'> <caller-input>',
|
|
26
27
|
'',
|
|
27
28
|
SHARED_REVIEW_INSTRUCTION,
|
|
28
29
|
].join('\n');
|
|
29
30
|
const POST_COMMIT_REVIEW_PROMPT = [
|
|
30
|
-
'Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.',
|
|
31
|
+
'Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.',
|
|
31
32
|
'',
|
|
32
33
|
'> <coder-output>',
|
|
33
34
|
'',
|
|
@@ -35,8 +36,6 @@ const POST_COMMIT_REVIEW_PROMPT = [
|
|
|
35
36
|
].join('\n');
|
|
36
37
|
const REBUTTAL_REVIEW_PROMPT = [
|
|
37
38
|
'No new commit was made because Coder rejected every finding.',
|
|
38
|
-
'For any rebuttal, accept or challenge it.',
|
|
39
|
-
'State which findings, if any, remain.',
|
|
40
39
|
'',
|
|
41
40
|
'> <coder-output>',
|
|
42
41
|
'',
|
|
@@ -85,20 +84,32 @@ const stateDescriptions = {
|
|
|
85
84
|
failed: 'Retains a recoverable REVIEW failure for the caller.',
|
|
86
85
|
done: 'The latest commit is approved with no unsettled findings.',
|
|
87
86
|
};
|
|
88
|
-
const playbookMeta = (stateId,
|
|
87
|
+
const playbookMeta = (stateId, role) => ({
|
|
89
88
|
playbook: {
|
|
90
89
|
stateId,
|
|
91
90
|
description: stateDescriptions[stateId],
|
|
92
|
-
...(
|
|
91
|
+
...(role === undefined ? {} : { role }),
|
|
93
92
|
},
|
|
94
93
|
});
|
|
95
94
|
const outputOf = (event) => event.output;
|
|
96
95
|
const isNonEmptyString = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
97
96
|
const stateMetadata = {
|
|
98
|
-
reviewInitial: {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
reviewInitial: {
|
|
98
|
+
sourceItem: 'REVIEW-1',
|
|
99
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
100
|
+
},
|
|
101
|
+
addressFindings: {
|
|
102
|
+
sourceItem: 'REVIEW-2',
|
|
103
|
+
asker: { kind: 'role', roleId: 'coder' },
|
|
104
|
+
},
|
|
105
|
+
reviewAfterCommit: {
|
|
106
|
+
sourceItem: 'REVIEW-3',
|
|
107
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
108
|
+
},
|
|
109
|
+
reviewAfterRebuttal: {
|
|
110
|
+
sourceItem: 'REVIEW-4',
|
|
111
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
112
|
+
},
|
|
102
113
|
};
|
|
103
114
|
const playerPlaceholder = fromPromise(async () => {
|
|
104
115
|
throw new Error('player actor must be provided by the runner');
|
|
@@ -264,10 +275,7 @@ export const reviewMachine = setup({
|
|
|
264
275
|
},
|
|
265
276
|
}).createMachine({
|
|
266
277
|
id: 'review',
|
|
267
|
-
context:
|
|
268
|
-
coderLlm: input.coderLlm,
|
|
269
|
-
reviewerLlm: input.reviewerLlm,
|
|
270
|
-
}),
|
|
278
|
+
context: {},
|
|
271
279
|
initial: 'ready',
|
|
272
280
|
on: {
|
|
273
281
|
BOSS_INTERRUPT: {
|
|
@@ -294,14 +302,14 @@ export const reviewMachine = setup({
|
|
|
294
302
|
reviewInitial: {
|
|
295
303
|
id: 'reviewInitial',
|
|
296
304
|
description: stateDescriptions.reviewInitial,
|
|
297
|
-
meta: playbookMeta('reviewInitial', '
|
|
305
|
+
meta: playbookMeta('reviewInitial', 'reviewer'),
|
|
298
306
|
tags: ['playbook.busy'],
|
|
299
307
|
invoke: {
|
|
300
308
|
src: 'player',
|
|
301
309
|
input: ({ context }) => ({
|
|
302
310
|
stateId: 'reviewInitial',
|
|
303
311
|
sourceItem: 'REVIEW-1',
|
|
304
|
-
|
|
312
|
+
role: 'reviewer',
|
|
305
313
|
prompt: INITIAL_REVIEW_PROMPT,
|
|
306
314
|
result: REVIEW_RESULTS,
|
|
307
315
|
callerInput: context.callerInput,
|
|
@@ -334,19 +342,17 @@ export const reviewMachine = setup({
|
|
|
334
342
|
addressFindings: {
|
|
335
343
|
id: 'addressFindings',
|
|
336
344
|
description: stateDescriptions.addressFindings,
|
|
337
|
-
meta: playbookMeta('addressFindings', '
|
|
345
|
+
meta: playbookMeta('addressFindings', 'coder'),
|
|
338
346
|
tags: ['playbook.busy'],
|
|
339
347
|
invoke: {
|
|
340
348
|
src: 'player',
|
|
341
349
|
input: ({ context }) => ({
|
|
342
350
|
stateId: 'addressFindings',
|
|
343
351
|
sourceItem: 'REVIEW-2',
|
|
344
|
-
|
|
352
|
+
role: 'coder',
|
|
345
353
|
prompt: CODER_DISPOSITION_PROMPT,
|
|
346
354
|
result: CODER_RESULTS,
|
|
347
355
|
reviewerOutput: context.reviewerOutput,
|
|
348
|
-
coderLlm: context.coderLlm,
|
|
349
|
-
reviewerLlm: context.reviewerLlm,
|
|
350
356
|
pendingBossQuestion: context.pendingBossQuestion,
|
|
351
357
|
bossReply: context.bossReply,
|
|
352
358
|
}),
|
|
@@ -376,14 +382,14 @@ export const reviewMachine = setup({
|
|
|
376
382
|
reviewAfterCommit: {
|
|
377
383
|
id: 'reviewAfterCommit',
|
|
378
384
|
description: stateDescriptions.reviewAfterCommit,
|
|
379
|
-
meta: playbookMeta('reviewAfterCommit', '
|
|
385
|
+
meta: playbookMeta('reviewAfterCommit', 'reviewer'),
|
|
380
386
|
tags: ['playbook.busy'],
|
|
381
387
|
invoke: {
|
|
382
388
|
src: 'player',
|
|
383
389
|
input: ({ context }) => ({
|
|
384
390
|
stateId: 'reviewAfterCommit',
|
|
385
391
|
sourceItem: 'REVIEW-3',
|
|
386
|
-
|
|
392
|
+
role: 'reviewer',
|
|
387
393
|
prompt: POST_COMMIT_REVIEW_PROMPT,
|
|
388
394
|
result: REVIEW_RESULTS,
|
|
389
395
|
coderOutput: context.coderOutput,
|
|
@@ -416,14 +422,14 @@ export const reviewMachine = setup({
|
|
|
416
422
|
reviewAfterRebuttal: {
|
|
417
423
|
id: 'reviewAfterRebuttal',
|
|
418
424
|
description: stateDescriptions.reviewAfterRebuttal,
|
|
419
|
-
meta: playbookMeta('reviewAfterRebuttal', '
|
|
425
|
+
meta: playbookMeta('reviewAfterRebuttal', 'reviewer'),
|
|
420
426
|
tags: ['playbook.busy'],
|
|
421
427
|
invoke: {
|
|
422
428
|
src: 'player',
|
|
423
429
|
input: ({ context }) => ({
|
|
424
430
|
stateId: 'reviewAfterRebuttal',
|
|
425
431
|
sourceItem: 'REVIEW-4',
|
|
426
|
-
|
|
432
|
+
role: 'reviewer',
|
|
427
433
|
prompt: REBUTTAL_REVIEW_PROMPT,
|
|
428
434
|
result: REBUTTAL_RESULTS,
|
|
429
435
|
coderOutput: context.coderOutput,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { assign, fromPromise, setup } from 'xstate';
|
|
8
8
|
|
|
9
|
-
type
|
|
9
|
+
type Role = 'coder' | 'reviewer';
|
|
10
10
|
|
|
11
11
|
type JumpableStateId = 'reviewInitial';
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ export interface PendingBossQuestion {
|
|
|
20
20
|
questionId: ResumableStateId;
|
|
21
21
|
resumeStateId: ResumableStateId;
|
|
22
22
|
sourceItem: `REVIEW-${1 | 2 | 3 | 4}`;
|
|
23
|
-
|
|
23
|
+
asker: { readonly kind: 'role'; readonly roleId: Role };
|
|
24
24
|
question: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -29,12 +29,9 @@ export interface ReviewOutput {
|
|
|
29
29
|
noUnsettledFindings: true;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export
|
|
33
|
-
coderLlm: string;
|
|
34
|
-
reviewerLlm: string;
|
|
35
|
-
}
|
|
32
|
+
export type ReviewInput = Readonly<Record<string, never>>;
|
|
36
33
|
|
|
37
|
-
export interface ReviewContext
|
|
34
|
+
export interface ReviewContext {
|
|
38
35
|
callerInput?: string;
|
|
39
36
|
reviewerOutput?: string;
|
|
40
37
|
coderOutput?: string;
|
|
@@ -59,14 +56,12 @@ export type ReviewEvent =
|
|
|
59
56
|
export interface PlayerInput {
|
|
60
57
|
stateId: ResumableStateId;
|
|
61
58
|
sourceItem: `REVIEW-${1 | 2 | 3 | 4}`;
|
|
62
|
-
|
|
59
|
+
role: Role;
|
|
63
60
|
prompt: string;
|
|
64
61
|
result: Readonly<Record<string, string>>;
|
|
65
62
|
callerInput?: string;
|
|
66
63
|
reviewerOutput?: string;
|
|
67
64
|
coderOutput?: string;
|
|
68
|
-
coderLlm?: string;
|
|
69
|
-
reviewerLlm?: string;
|
|
70
65
|
pendingBossQuestion?: PendingBossQuestion;
|
|
71
66
|
bossReply?: string;
|
|
72
67
|
}
|
|
@@ -86,6 +81,7 @@ const NEEDS_BOSS_REPLY_DESCRIPTION =
|
|
|
86
81
|
const SHARED_REVIEW_INSTRUCTION = [
|
|
87
82
|
'Understand the full picture and think systematically about the underlying design.',
|
|
88
83
|
'Continue to identify issues or improvements, if any (numbered; no duplication).',
|
|
84
|
+
'For any rebuttal, accept or challenge it.',
|
|
89
85
|
'Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.',
|
|
90
86
|
'Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.',
|
|
91
87
|
'For specs, flag stale, missing, over-specified, or under-specified ones.',
|
|
@@ -100,7 +96,7 @@ const SHARED_REVIEW_INSTRUCTION = [
|
|
|
100
96
|
|
|
101
97
|
const INITIAL_REVIEW_PROMPT = [
|
|
102
98
|
'A new review begins on the latest commit.',
|
|
103
|
-
'Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.',
|
|
99
|
+
'Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.',
|
|
104
100
|
'',
|
|
105
101
|
'> <caller-input>',
|
|
106
102
|
'',
|
|
@@ -108,7 +104,7 @@ const INITIAL_REVIEW_PROMPT = [
|
|
|
108
104
|
].join('\n');
|
|
109
105
|
|
|
110
106
|
const POST_COMMIT_REVIEW_PROMPT = [
|
|
111
|
-
'Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.',
|
|
107
|
+
'Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.',
|
|
112
108
|
'',
|
|
113
109
|
'> <coder-output>',
|
|
114
110
|
'',
|
|
@@ -117,8 +113,6 @@ const POST_COMMIT_REVIEW_PROMPT = [
|
|
|
117
113
|
|
|
118
114
|
const REBUTTAL_REVIEW_PROMPT = [
|
|
119
115
|
'No new commit was made because Coder rejected every finding.',
|
|
120
|
-
'For any rebuttal, accept or challenge it.',
|
|
121
|
-
'State which findings, if any, remain.',
|
|
122
116
|
'',
|
|
123
117
|
'> <coder-output>',
|
|
124
118
|
'',
|
|
@@ -184,12 +178,12 @@ const stateDescriptions = {
|
|
|
184
178
|
|
|
185
179
|
const playbookMeta = <StateId extends keyof typeof stateDescriptions>(
|
|
186
180
|
stateId: StateId,
|
|
187
|
-
|
|
181
|
+
role?: Role,
|
|
188
182
|
) => ({
|
|
189
183
|
playbook: {
|
|
190
184
|
stateId,
|
|
191
185
|
description: stateDescriptions[stateId],
|
|
192
|
-
...(
|
|
186
|
+
...(role === undefined ? {} : { role }),
|
|
193
187
|
},
|
|
194
188
|
});
|
|
195
189
|
|
|
@@ -200,13 +194,28 @@ const isNonEmptyString = (value: unknown): value is string =>
|
|
|
200
194
|
typeof value === 'string' && value.trim().length > 0;
|
|
201
195
|
|
|
202
196
|
const stateMetadata = {
|
|
203
|
-
reviewInitial: {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
197
|
+
reviewInitial: {
|
|
198
|
+
sourceItem: 'REVIEW-1',
|
|
199
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
200
|
+
},
|
|
201
|
+
addressFindings: {
|
|
202
|
+
sourceItem: 'REVIEW-2',
|
|
203
|
+
asker: { kind: 'role', roleId: 'coder' },
|
|
204
|
+
},
|
|
205
|
+
reviewAfterCommit: {
|
|
206
|
+
sourceItem: 'REVIEW-3',
|
|
207
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
208
|
+
},
|
|
209
|
+
reviewAfterRebuttal: {
|
|
210
|
+
sourceItem: 'REVIEW-4',
|
|
211
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
212
|
+
},
|
|
207
213
|
} as const satisfies Record<
|
|
208
214
|
ResumableStateId,
|
|
209
|
-
{
|
|
215
|
+
{
|
|
216
|
+
sourceItem: `REVIEW-${1 | 2 | 3 | 4}`;
|
|
217
|
+
asker: { readonly kind: 'role'; readonly roleId: Role };
|
|
218
|
+
}
|
|
210
219
|
>;
|
|
211
220
|
|
|
212
221
|
const playerPlaceholder = fromPromise<PlayerOutput, PlayerInput>(async () => {
|
|
@@ -392,10 +401,7 @@ export const reviewMachine = setup({
|
|
|
392
401
|
},
|
|
393
402
|
}).createMachine({
|
|
394
403
|
id: 'review',
|
|
395
|
-
context:
|
|
396
|
-
coderLlm: input.coderLlm,
|
|
397
|
-
reviewerLlm: input.reviewerLlm,
|
|
398
|
-
}),
|
|
404
|
+
context: {},
|
|
399
405
|
initial: 'ready',
|
|
400
406
|
on: {
|
|
401
407
|
BOSS_INTERRUPT: {
|
|
@@ -422,14 +428,14 @@ export const reviewMachine = setup({
|
|
|
422
428
|
reviewInitial: {
|
|
423
429
|
id: 'reviewInitial',
|
|
424
430
|
description: stateDescriptions.reviewInitial,
|
|
425
|
-
meta: playbookMeta('reviewInitial', '
|
|
431
|
+
meta: playbookMeta('reviewInitial', 'reviewer'),
|
|
426
432
|
tags: ['playbook.busy'],
|
|
427
433
|
invoke: {
|
|
428
434
|
src: 'player',
|
|
429
435
|
input: ({ context }): PlayerInput => ({
|
|
430
436
|
stateId: 'reviewInitial',
|
|
431
437
|
sourceItem: 'REVIEW-1',
|
|
432
|
-
|
|
438
|
+
role: 'reviewer',
|
|
433
439
|
prompt: INITIAL_REVIEW_PROMPT,
|
|
434
440
|
result: REVIEW_RESULTS,
|
|
435
441
|
callerInput: context.callerInput,
|
|
@@ -462,19 +468,17 @@ export const reviewMachine = setup({
|
|
|
462
468
|
addressFindings: {
|
|
463
469
|
id: 'addressFindings',
|
|
464
470
|
description: stateDescriptions.addressFindings,
|
|
465
|
-
meta: playbookMeta('addressFindings', '
|
|
471
|
+
meta: playbookMeta('addressFindings', 'coder'),
|
|
466
472
|
tags: ['playbook.busy'],
|
|
467
473
|
invoke: {
|
|
468
474
|
src: 'player',
|
|
469
475
|
input: ({ context }): PlayerInput => ({
|
|
470
476
|
stateId: 'addressFindings',
|
|
471
477
|
sourceItem: 'REVIEW-2',
|
|
472
|
-
|
|
478
|
+
role: 'coder',
|
|
473
479
|
prompt: CODER_DISPOSITION_PROMPT,
|
|
474
480
|
result: CODER_RESULTS,
|
|
475
481
|
reviewerOutput: context.reviewerOutput,
|
|
476
|
-
coderLlm: context.coderLlm,
|
|
477
|
-
reviewerLlm: context.reviewerLlm,
|
|
478
482
|
pendingBossQuestion: context.pendingBossQuestion,
|
|
479
483
|
bossReply: context.bossReply,
|
|
480
484
|
}),
|
|
@@ -504,14 +508,14 @@ export const reviewMachine = setup({
|
|
|
504
508
|
reviewAfterCommit: {
|
|
505
509
|
id: 'reviewAfterCommit',
|
|
506
510
|
description: stateDescriptions.reviewAfterCommit,
|
|
507
|
-
meta: playbookMeta('reviewAfterCommit', '
|
|
511
|
+
meta: playbookMeta('reviewAfterCommit', 'reviewer'),
|
|
508
512
|
tags: ['playbook.busy'],
|
|
509
513
|
invoke: {
|
|
510
514
|
src: 'player',
|
|
511
515
|
input: ({ context }): PlayerInput => ({
|
|
512
516
|
stateId: 'reviewAfterCommit',
|
|
513
517
|
sourceItem: 'REVIEW-3',
|
|
514
|
-
|
|
518
|
+
role: 'reviewer',
|
|
515
519
|
prompt: POST_COMMIT_REVIEW_PROMPT,
|
|
516
520
|
result: REVIEW_RESULTS,
|
|
517
521
|
coderOutput: context.coderOutput,
|
|
@@ -544,14 +548,14 @@ export const reviewMachine = setup({
|
|
|
544
548
|
reviewAfterRebuttal: {
|
|
545
549
|
id: 'reviewAfterRebuttal',
|
|
546
550
|
description: stateDescriptions.reviewAfterRebuttal,
|
|
547
|
-
meta: playbookMeta('reviewAfterRebuttal', '
|
|
551
|
+
meta: playbookMeta('reviewAfterRebuttal', 'reviewer'),
|
|
548
552
|
tags: ['playbook.busy'],
|
|
549
553
|
invoke: {
|
|
550
554
|
src: 'player',
|
|
551
555
|
input: ({ context }): PlayerInput => ({
|
|
552
556
|
stateId: 'reviewAfterRebuttal',
|
|
553
557
|
sourceItem: 'REVIEW-4',
|
|
554
|
-
|
|
558
|
+
role: 'reviewer',
|
|
555
559
|
prompt: REBUTTAL_REVIEW_PROMPT,
|
|
556
560
|
result: REBUTTAL_RESULTS,
|
|
557
561
|
coderOutput: context.coderOutput,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# REVIEW: Commit Review Workflow
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Roles:
|
|
7
7
|
|
|
8
8
|
- Coder
|
|
9
9
|
- Reviewer
|
|
@@ -13,12 +13,13 @@ Players:
|
|
|
13
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
14
|
|
|
15
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.
|
|
16
|
+
> Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.
|
|
17
17
|
>
|
|
18
18
|
> > <caller-input>
|
|
19
19
|
>
|
|
20
20
|
> Understand the full picture and think systematically about the underlying design.
|
|
21
21
|
> Continue to identify issues or improvements, if any (numbered; no duplication).
|
|
22
|
+
> For any rebuttal, accept or challenge it.
|
|
22
23
|
> Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
|
|
23
24
|
> Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
|
|
24
25
|
> For specs, flag stale, missing, over-specified, or under-specified ones.
|
|
@@ -63,12 +64,13 @@ Results:
|
|
|
63
64
|
|
|
64
65
|
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
|
|
|
66
|
-
> Review the latest commit and resulting repository state; read the commit message first for its intent and rationale.
|
|
67
|
+
> Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.
|
|
67
68
|
>
|
|
68
69
|
> > <coder-output>
|
|
69
70
|
>
|
|
70
71
|
> Understand the full picture and think systematically about the underlying design.
|
|
71
72
|
> Continue to identify issues or improvements, if any (numbered; no duplication).
|
|
73
|
+
> For any rebuttal, accept or challenge it.
|
|
72
74
|
> Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
|
|
73
75
|
> Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
|
|
74
76
|
> For specs, flag stale, missing, over-specified, or under-specified ones.
|
|
@@ -89,13 +91,12 @@ Results:
|
|
|
89
91
|
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
92
|
|
|
91
93
|
> 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
94
|
>
|
|
95
95
|
> > <coder-output>
|
|
96
96
|
>
|
|
97
97
|
> Understand the full picture and think systematically about the underlying design.
|
|
98
98
|
> Continue to identify issues or improvements, if any (numbered; no duplication).
|
|
99
|
+
> For any rebuttal, accept or challenge it.
|
|
99
100
|
> Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
|
|
100
101
|
> Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
|
|
101
102
|
> For specs, flag stale, missing, over-specified, or under-specified ones.
|