@sublang/playbook 11.0.0 → 12.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/docs/cli.md +9 -6
- package/docs/configuration.md +15 -1
- package/package.json +22 -3
- package/reference/sdlc/code.playbook/code.fsm.d.ts +22 -19
- package/reference/sdlc/code.playbook/code.fsm.js +116 -52
- package/reference/sdlc/code.playbook/code.fsm.ts +149 -64
- package/reference/sdlc/code.playbook/code.gears.md +40 -20
- package/reference/sdlc/code.playbook/code.playbook.js +23 -2
- package/reference/sdlc/code.playbook/code.playbook.ts +23 -2
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +18 -2
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +13 -6
- package/reference/sdlc/decide.playbook/decide.fsm.js +54 -27
- package/reference/sdlc/decide.playbook/decide.fsm.ts +68 -29
- package/reference/sdlc/decide.playbook/decide.gears.md +25 -19
- package/reference/sdlc/decide.playbook/decide.playbook.js +11 -3
- package/reference/sdlc/decide.playbook/decide.playbook.ts +11 -3
- package/reference/sdlc/decide.playbook/decide.registry.js +1 -1
- package/reference/sdlc/decide.playbook/decide.registry.ts +1 -1
- package/reference/sdlc/dev.md +52 -0
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +261 -0
- package/reference/sdlc/dev.playbook/dev.fsm.js +723 -0
- package/reference/sdlc/dev.playbook/dev.fsm.ts +988 -0
- package/reference/sdlc/dev.playbook/dev.gears.md +91 -0
- package/reference/sdlc/dev.playbook/dev.playbook.d.ts +21 -0
- package/reference/sdlc/dev.playbook/dev.playbook.js +143 -0
- package/reference/sdlc/dev.playbook/dev.playbook.ts +246 -0
- package/reference/sdlc/dev.playbook/dev.registry.d.ts +40 -0
- package/reference/sdlc/dev.playbook/dev.registry.js +64 -0
- package/reference/sdlc/dev.playbook/dev.registry.ts +120 -0
- package/reference/sdlc/review.playbook/review.fsm.d.ts +15 -2
- package/reference/sdlc/review.playbook/review.fsm.js +77 -27
- package/reference/sdlc/review.playbook/review.fsm.ts +96 -30
- package/reference/sdlc/review.playbook/review.gears.md +52 -26
- package/reference/sdlc/review.playbook/review.playbook.js +17 -7
- package/reference/sdlc/review.playbook/review.playbook.ts +17 -7
- package/reference/sdlc/review.playbook/review.registry.js +1 -1
- package/reference/sdlc/review.playbook/review.registry.ts +1 -1
- package/slc/link.md +12 -5
- package/slc/text2gears.md +3 -0
- package/src/xstate-playbook-runtime.js +5 -2
- package/src/xstate-playbook-runtime.ts +5 -2
- package/src/xstate-runtime.js +13 -1
- package/src/xstate-runtime.ts +13 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
|
|
4
|
+
import createPlaybookRuntime, {
|
|
5
|
+
type DevPlaybookHostCapabilities,
|
|
6
|
+
type PlaybookRuntime,
|
|
7
|
+
} from './dev.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 type DevOptions = Readonly<Record<string, never>>;
|
|
19
|
+
|
|
20
|
+
export interface DevPlaybookRegistryEntry {
|
|
21
|
+
id: 'dev';
|
|
22
|
+
command: 'dev';
|
|
23
|
+
intent: string;
|
|
24
|
+
artifactSchema: 3;
|
|
25
|
+
runtimeProfile: {
|
|
26
|
+
readonly kind: 'shared-factory';
|
|
27
|
+
readonly compat: {
|
|
28
|
+
readonly artifactSchema: 3;
|
|
29
|
+
readonly runtimeAbi: number;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
requiredRoleIds: readonly ['analyst'];
|
|
33
|
+
concurrentRoleSets: readonly [];
|
|
34
|
+
summaryPolicy: PlaybookSummaryPolicy;
|
|
35
|
+
validateOptions(optionSlice: unknown): DevOptions;
|
|
36
|
+
createRuntime(
|
|
37
|
+
options: DevOptions,
|
|
38
|
+
hostCapabilities: DevPlaybookHostCapabilities,
|
|
39
|
+
): PlaybookRuntime;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// CODE and DECIDE own and label the rounds of the paths DEV starts. DEV's
|
|
43
|
+
// suspended call states only delegate to those children and must not
|
|
44
|
+
// double-count their rounds; only the Analyst planning rounds are DEV's own.
|
|
45
|
+
export const devStateCountLabels = {
|
|
46
|
+
planAnalysis: 'planning round',
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
export const devCopyPasteGuardNames = ['code', 'decideThenCode'] as const;
|
|
50
|
+
|
|
51
|
+
function countNoun(
|
|
52
|
+
count: number,
|
|
53
|
+
singular: string,
|
|
54
|
+
plural = `${singular}s`,
|
|
55
|
+
): string {
|
|
56
|
+
return `${count} ${count === 1 ? singular : plural}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function devSavedCountsLine(
|
|
60
|
+
counts: { interruptions: number; copyPastes: number },
|
|
61
|
+
rounds: number,
|
|
62
|
+
): string {
|
|
63
|
+
return [
|
|
64
|
+
'Saved you',
|
|
65
|
+
countNoun(counts.interruptions, 'interruption'),
|
|
66
|
+
'and',
|
|
67
|
+
countNoun(counts.copyPastes, 'copy-paste'),
|
|
68
|
+
'across',
|
|
69
|
+
countNoun(rounds, 'round'),
|
|
70
|
+
'of planning.',
|
|
71
|
+
].join(' ');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const devSummaryPolicy: PlaybookSummaryPolicy = {
|
|
75
|
+
stateCountLabels: devStateCountLabels,
|
|
76
|
+
copyPasteGuardNames: devCopyPasteGuardNames,
|
|
77
|
+
savedCountsLine: devSavedCountsLine,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export function validateDevOptions(optionSlice: unknown): DevOptions {
|
|
81
|
+
if (optionSlice === undefined) return Object.freeze({});
|
|
82
|
+
if (
|
|
83
|
+
optionSlice === null ||
|
|
84
|
+
typeof optionSlice !== 'object' ||
|
|
85
|
+
Array.isArray(optionSlice)
|
|
86
|
+
) {
|
|
87
|
+
throw new Error('captain.options.playbooks.dev.options must be an object');
|
|
88
|
+
}
|
|
89
|
+
const keys = Object.keys(optionSlice);
|
|
90
|
+
if (keys.length > 0) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Unknown config field captain.options.playbooks.dev.options.${keys[0]}`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return Object.freeze({});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const devPlaybookRegistryEntry: DevPlaybookRegistryEntry = {
|
|
99
|
+
id: 'dev',
|
|
100
|
+
command: 'dev',
|
|
101
|
+
intent:
|
|
102
|
+
'analyze a development request that needs planning before choosing direct implementation or a durable decision first',
|
|
103
|
+
artifactSchema: 3,
|
|
104
|
+
runtimeProfile: Object.freeze({
|
|
105
|
+
kind: 'shared-factory',
|
|
106
|
+
compat: createPlaybookRuntime.compat,
|
|
107
|
+
}),
|
|
108
|
+
requiredRoleIds: ['analyst'],
|
|
109
|
+
concurrentRoleSets: [],
|
|
110
|
+
summaryPolicy: devSummaryPolicy,
|
|
111
|
+
validateOptions: validateDevOptions,
|
|
112
|
+
createRuntime(options, hostCapabilities) {
|
|
113
|
+
return createPlaybookRuntime({
|
|
114
|
+
configuredOptions: options,
|
|
115
|
+
hostCapabilities,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export default devPlaybookRegistryEntry;
|
|
@@ -12,14 +12,24 @@ export interface PendingBossQuestion {
|
|
|
12
12
|
question: string;
|
|
13
13
|
}
|
|
14
14
|
export interface ReviewOutput {
|
|
15
|
-
approvedCommit: 'latest';
|
|
16
15
|
noUnsettledFindings: true;
|
|
16
|
+
/**
|
|
17
|
+
* Exact receipt-derived repository revision at which the review scope was
|
|
18
|
+
* evaluated: the closing clean round's `unchanged` receipt observes it as
|
|
19
|
+
* HEAD — the last review-fix commit when one landed, or the caller-supplied
|
|
20
|
+
* scope revision when none did (DR-045).
|
|
21
|
+
*/
|
|
22
|
+
evaluatedRevision: string;
|
|
17
23
|
}
|
|
18
24
|
export type ReviewInput = Readonly<Record<string, never>>;
|
|
19
25
|
export interface ReviewContext {
|
|
20
26
|
callerInput?: string;
|
|
21
27
|
reviewerOutput?: string;
|
|
22
28
|
coderOutput?: string;
|
|
29
|
+
/** Receipt-derived OID of the latest review-fix commit (never player prose). */
|
|
30
|
+
latestCommit?: string;
|
|
31
|
+
/** Observed HEAD of the closing clean round's unchanged receipt (DR-045). */
|
|
32
|
+
evaluatedRevision?: string;
|
|
23
33
|
lastError?: unknown;
|
|
24
34
|
pendingBossQuestion?: PendingBossQuestion;
|
|
25
35
|
bossReply?: string;
|
|
@@ -45,6 +55,7 @@ export interface PlayerInput {
|
|
|
45
55
|
callerInput?: string;
|
|
46
56
|
reviewerOutput?: string;
|
|
47
57
|
coderOutput?: string;
|
|
58
|
+
latestCommit?: string;
|
|
48
59
|
pendingBossQuestion?: PendingBossQuestion;
|
|
49
60
|
bossReply?: string;
|
|
50
61
|
}
|
|
@@ -53,9 +64,11 @@ export type PlayerOutput = {
|
|
|
53
64
|
reviewerOutput: string;
|
|
54
65
|
} | {
|
|
55
66
|
guard: 'noFindings';
|
|
67
|
+
evaluatedRevision: string;
|
|
56
68
|
} | {
|
|
57
69
|
guard: 'committed';
|
|
58
70
|
coderOutput: string;
|
|
71
|
+
latestCommit: string;
|
|
59
72
|
} | {
|
|
60
73
|
guard: 'rejectedAll';
|
|
61
74
|
coderOutput: string;
|
|
@@ -109,7 +122,7 @@ export declare const reviewMachine: import("xstate").StateMachine<ReviewContext,
|
|
|
109
122
|
type: "rememberCoderOutput";
|
|
110
123
|
params: import("xstate").NonReducibleUnknown;
|
|
111
124
|
} | {
|
|
112
|
-
type: "
|
|
125
|
+
type: "rememberEvaluatedRevision";
|
|
113
126
|
params: import("xstate").NonReducibleUnknown;
|
|
114
127
|
} | {
|
|
115
128
|
type: "setPendingReviewInitial";
|
|
@@ -6,13 +6,16 @@ import { assign, fromPromise, setup } from 'xstate';
|
|
|
6
6
|
const NEEDS_BOSS_REPLY_DESCRIPTION = "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.";
|
|
7
7
|
const SHARED_REVIEW_INSTRUCTION = [
|
|
8
8
|
'Understand the full picture and think systematically about the underlying design.',
|
|
9
|
-
'Continue to identify issues or improvements, if any
|
|
10
|
-
'
|
|
11
|
-
'Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.',
|
|
9
|
+
'Continue to identify issues or improvements, if any, without duplication.',
|
|
10
|
+
'Number the findings consistently across rounds.',
|
|
12
11
|
'Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.',
|
|
13
|
-
'For specs, flag stale, missing, over-specified, or under-specified ones.',
|
|
12
|
+
'For specs, flag stale, missing, over-specified, or under-specified ones, if any.',
|
|
14
13
|
'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.',
|
|
15
14
|
'',
|
|
15
|
+
'If an issue represents a class of defect, find every instance within the review scope worth fixing rather than surfacing one or two per round, which drags out the review.',
|
|
16
|
+
'For any rebuttal, accept or challenge it.',
|
|
17
|
+
'Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.',
|
|
18
|
+
'',
|
|
16
19
|
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
17
20
|
'Do not edit files or commit; report findings only.',
|
|
18
21
|
'',
|
|
@@ -20,69 +23,79 @@ const SHARED_REVIEW_INSTRUCTION = [
|
|
|
20
23
|
'Consult @specs/meta.md for spec requirements if needed; verify affected specs follow it.',
|
|
21
24
|
].join('\n');
|
|
22
25
|
const INITIAL_REVIEW_PROMPT = [
|
|
23
|
-
'A new review begins
|
|
24
|
-
'
|
|
26
|
+
'A new review begins for the review scope.',
|
|
27
|
+
'Keep to the original intent and follow what it asks.',
|
|
28
|
+
'When the scope names commits, read each commit message for its context and rationale; otherwise use repository history and commit messages wherever they help establish that context.',
|
|
25
29
|
'',
|
|
26
30
|
'> <caller-input>',
|
|
27
31
|
'',
|
|
28
32
|
SHARED_REVIEW_INSTRUCTION,
|
|
29
33
|
].join('\n');
|
|
30
34
|
const POST_COMMIT_REVIEW_PROMPT = [
|
|
31
|
-
'
|
|
35
|
+
'A new review round begins for the review scope in the cumulative committed state, with particular attention to the latest review-fix commit.',
|
|
36
|
+
'Keep to the original intent and follow what it asks.',
|
|
37
|
+
"Read the latest review-fix commit's message and see Coder's feedback below.",
|
|
32
38
|
'',
|
|
39
|
+
'> <caller-input>',
|
|
40
|
+
'> <latest-commit>',
|
|
33
41
|
'> <coder-output>',
|
|
34
42
|
'',
|
|
35
43
|
SHARED_REVIEW_INSTRUCTION,
|
|
36
44
|
].join('\n');
|
|
37
45
|
const REBUTTAL_REVIEW_PROMPT = [
|
|
38
46
|
'No new commit was made because Coder rejected every finding.',
|
|
47
|
+
"See Coder's feedback below.",
|
|
39
48
|
'',
|
|
49
|
+
'> <caller-input>',
|
|
40
50
|
'> <coder-output>',
|
|
41
51
|
'',
|
|
42
52
|
SHARED_REVIEW_INSTRUCTION,
|
|
43
53
|
].join('\n');
|
|
44
54
|
const CODER_DISPOSITION_PROMPT = [
|
|
55
|
+
'> <caller-input>',
|
|
45
56
|
'> <reviewer-output>',
|
|
46
57
|
'',
|
|
47
58
|
'For each review item, accept or reject it.',
|
|
48
59
|
'Before deciding, understand the full picture and think systematically about the underlying design.',
|
|
60
|
+
'Keep to the original intent and follow what it asks.',
|
|
49
61
|
'Reject anything that is not essential or is not worth fixing now.',
|
|
50
|
-
'If you accept an item, fix its root cause, including any fundamental design flaw
|
|
62
|
+
'If you accept an item, fix its root cause, including any fundamental design flaw — do not patch around it; if it represents a class of defect, find every instance within the review scope worth fixing rather than addressing one or two per round, which drags out the review.',
|
|
51
63
|
'If you reject an item, give the reasoning and cite code or test output that supports it.',
|
|
52
64
|
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
53
65
|
'',
|
|
54
|
-
'If you accept any item, make minimal changes and
|
|
66
|
+
'If you accept any item, make minimal changes and add one new review-fix commit; never rewrite any existing commit.',
|
|
55
67
|
'Follow @specs/packages/git.md.',
|
|
56
68
|
'Make the commit message explain concisely what changed and why, including relevant verification.',
|
|
57
|
-
'
|
|
69
|
+
'Identify every new commit you make.',
|
|
70
|
+
'Coder is <coder-llm>; Reviewer is <reviewer-llm>.',
|
|
58
71
|
'',
|
|
59
72
|
'If you reject every item, change nothing and make no commit.',
|
|
60
73
|
'Report every disposition, all relevant run results, and every rebuttal.',
|
|
61
74
|
].join('\n');
|
|
62
75
|
const REVIEW_RESULTS = {
|
|
63
76
|
hasFindings: 'Reviewer raised one or more unsettled findings. Output shall include `reviewerOutput: <verbatim final text>`.',
|
|
64
|
-
noFindings: 'Reviewer
|
|
77
|
+
noFindings: 'Reviewer affirmatively reported the requested review complete with no unsettled findings remaining; a progress report, status update, or promise of a later result supports no review outcome. Output shall include `evaluatedRevision: <repository revision>`.',
|
|
65
78
|
needsBossReply: NEEDS_BOSS_REPLY_DESCRIPTION,
|
|
66
79
|
};
|
|
67
80
|
const REBUTTAL_RESULTS = {
|
|
68
81
|
hasFindings: 'Reviewer kept one or more unsettled findings. Output shall include `reviewerOutput: <verbatim final text>`.',
|
|
69
|
-
noFindings: 'Reviewer accepted the rebuttals and no unsettled findings
|
|
82
|
+
noFindings: 'Reviewer accepted the rebuttals and affirmatively reported the requested review complete with no unsettled findings remaining; a progress report, status update, or promise of a later result supports no review outcome. Output shall include `evaluatedRevision: <repository revision>`.',
|
|
70
83
|
needsBossReply: NEEDS_BOSS_REPLY_DESCRIPTION,
|
|
71
84
|
};
|
|
72
85
|
const CODER_RESULTS = {
|
|
73
|
-
committed: 'Coder accepted at least one item and
|
|
86
|
+
committed: 'Coder accepted at least one item and added one new review-fix commit. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.',
|
|
74
87
|
rejectedAll: 'Coder rejected every item and made no commit. Output shall include `coderOutput: <verbatim final text>`.',
|
|
75
88
|
needsBossReply: NEEDS_BOSS_REPLY_DESCRIPTION,
|
|
76
89
|
};
|
|
77
90
|
const stateDescriptions = {
|
|
78
|
-
ready: 'Waits for a caller to start a committed-work review.',
|
|
79
|
-
reviewInitial:
|
|
91
|
+
ready: 'Waits for a caller to start a scoped committed-work review.',
|
|
92
|
+
reviewInitial: "REVIEW-1: Reviewer reviews the caller's review scope against the original intent.",
|
|
80
93
|
addressFindings: 'REVIEW-2: Coder accepts or rejects every current review finding.',
|
|
81
|
-
reviewAfterCommit: 'REVIEW-3: Reviewer
|
|
94
|
+
reviewAfterCommit: 'REVIEW-3: Reviewer reviews the cumulative committed state after a review-fix commit.',
|
|
82
95
|
reviewAfterRebuttal: 'REVIEW-4: Reviewer adjudicates an all-rejected Coder disposition.',
|
|
83
96
|
awaitBossReply: 'Waits for Boss to answer the active player question.',
|
|
84
97
|
failed: 'Retains a recoverable REVIEW failure for the caller.',
|
|
85
|
-
done: 'The
|
|
98
|
+
done: 'The requested review is complete: no unsettled findings remain within the review scope.',
|
|
86
99
|
};
|
|
87
100
|
const playbookMeta = (stateId, role) => ({
|
|
88
101
|
playbook: {
|
|
@@ -130,10 +143,16 @@ export const reviewMachine = setup({
|
|
|
130
143
|
return (output.guard === 'hasFindings' &&
|
|
131
144
|
isNonEmptyString(output.reviewerOutput));
|
|
132
145
|
},
|
|
133
|
-
noFindings: ({ event }) =>
|
|
146
|
+
noFindings: ({ event }) => {
|
|
147
|
+
const output = outputOf(event);
|
|
148
|
+
return (output.guard === 'noFindings' &&
|
|
149
|
+
isNonEmptyString(output.evaluatedRevision));
|
|
150
|
+
},
|
|
134
151
|
committed: ({ event }) => {
|
|
135
152
|
const output = outputOf(event);
|
|
136
|
-
return (output.guard === 'committed' &&
|
|
153
|
+
return (output.guard === 'committed' &&
|
|
154
|
+
isNonEmptyString(output.coderOutput) &&
|
|
155
|
+
isNonEmptyString(output.latestCommit));
|
|
137
156
|
},
|
|
138
157
|
rejectedAll: ({ event }) => {
|
|
139
158
|
const output = outputOf(event);
|
|
@@ -175,6 +194,8 @@ export const reviewMachine = setup({
|
|
|
175
194
|
callerInput: ({ event }) => event.type === 'START_REVIEW' ? event.callerInput : undefined,
|
|
176
195
|
reviewerOutput: undefined,
|
|
177
196
|
coderOutput: undefined,
|
|
197
|
+
latestCommit: undefined,
|
|
198
|
+
evaluatedRevision: undefined,
|
|
178
199
|
lastError: undefined,
|
|
179
200
|
pendingBossQuestion: undefined,
|
|
180
201
|
bossReply: undefined,
|
|
@@ -183,6 +204,8 @@ export const reviewMachine = setup({
|
|
|
183
204
|
callerInput: ({ event }) => event.type === 'BOSS_INTERRUPT' ? event.bossIntent : undefined,
|
|
184
205
|
reviewerOutput: undefined,
|
|
185
206
|
coderOutput: undefined,
|
|
207
|
+
latestCommit: undefined,
|
|
208
|
+
evaluatedRevision: undefined,
|
|
186
209
|
lastError: undefined,
|
|
187
210
|
pendingBossQuestion: undefined,
|
|
188
211
|
bossReply: undefined,
|
|
@@ -205,11 +228,29 @@ export const reviewMachine = setup({
|
|
|
205
228
|
? output.coderOutput
|
|
206
229
|
: undefined;
|
|
207
230
|
},
|
|
231
|
+
// The receipt-derived review-fix commit identity replaces the prior one
|
|
232
|
+
// only when Coder committed; an all-rejected round leaves the evaluated
|
|
233
|
+
// revision unchanged.
|
|
234
|
+
latestCommit: ({ context, event }) => {
|
|
235
|
+
const output = outputOf(event);
|
|
236
|
+
return output.guard === 'committed'
|
|
237
|
+
? output.latestCommit
|
|
238
|
+
: context.latestCommit;
|
|
239
|
+
},
|
|
208
240
|
lastError: undefined,
|
|
209
241
|
pendingBossQuestion: undefined,
|
|
210
242
|
bossReply: undefined,
|
|
211
243
|
}),
|
|
212
|
-
|
|
244
|
+
rememberEvaluatedRevision: assign({
|
|
245
|
+
// DR-045: the closing clean round's unchanged receipt observes the
|
|
246
|
+
// exact revision the review scope was evaluated at.
|
|
247
|
+
evaluatedRevision: ({ context, event }) => {
|
|
248
|
+
const output = outputOf(event);
|
|
249
|
+
return output.guard === 'noFindings'
|
|
250
|
+
? output.evaluatedRevision
|
|
251
|
+
: context.evaluatedRevision;
|
|
252
|
+
},
|
|
253
|
+
lastError: undefined,
|
|
213
254
|
pendingBossQuestion: undefined,
|
|
214
255
|
bossReply: undefined,
|
|
215
256
|
}),
|
|
@@ -345,7 +386,7 @@ export const reviewMachine = setup({
|
|
|
345
386
|
acceptedOutcome: 'noFindings',
|
|
346
387
|
},
|
|
347
388
|
},
|
|
348
|
-
'
|
|
389
|
+
'rememberEvaluatedRevision',
|
|
349
390
|
],
|
|
350
391
|
},
|
|
351
392
|
{
|
|
@@ -383,6 +424,7 @@ export const reviewMachine = setup({
|
|
|
383
424
|
role: 'coder',
|
|
384
425
|
prompt: CODER_DISPOSITION_PROMPT,
|
|
385
426
|
result: CODER_RESULTS,
|
|
427
|
+
callerInput: context.callerInput,
|
|
386
428
|
reviewerOutput: context.reviewerOutput,
|
|
387
429
|
pendingBossQuestion: context.pendingBossQuestion,
|
|
388
430
|
bossReply: context.bossReply,
|
|
@@ -453,6 +495,8 @@ export const reviewMachine = setup({
|
|
|
453
495
|
role: 'reviewer',
|
|
454
496
|
prompt: POST_COMMIT_REVIEW_PROMPT,
|
|
455
497
|
result: REVIEW_RESULTS,
|
|
498
|
+
callerInput: context.callerInput,
|
|
499
|
+
latestCommit: context.latestCommit,
|
|
456
500
|
coderOutput: context.coderOutput,
|
|
457
501
|
pendingBossQuestion: context.pendingBossQuestion,
|
|
458
502
|
bossReply: context.bossReply,
|
|
@@ -485,7 +529,7 @@ export const reviewMachine = setup({
|
|
|
485
529
|
acceptedOutcome: 'noFindings',
|
|
486
530
|
},
|
|
487
531
|
},
|
|
488
|
-
'
|
|
532
|
+
'rememberEvaluatedRevision',
|
|
489
533
|
],
|
|
490
534
|
},
|
|
491
535
|
{
|
|
@@ -523,6 +567,7 @@ export const reviewMachine = setup({
|
|
|
523
567
|
role: 'reviewer',
|
|
524
568
|
prompt: REBUTTAL_REVIEW_PROMPT,
|
|
525
569
|
result: REBUTTAL_RESULTS,
|
|
570
|
+
callerInput: context.callerInput,
|
|
526
571
|
coderOutput: context.coderOutput,
|
|
527
572
|
pendingBossQuestion: context.pendingBossQuestion,
|
|
528
573
|
bossReply: context.bossReply,
|
|
@@ -555,7 +600,7 @@ export const reviewMachine = setup({
|
|
|
555
600
|
acceptedOutcome: 'noFindings',
|
|
556
601
|
},
|
|
557
602
|
},
|
|
558
|
-
'
|
|
603
|
+
'rememberEvaluatedRevision',
|
|
559
604
|
],
|
|
560
605
|
},
|
|
561
606
|
{
|
|
@@ -644,8 +689,13 @@ export const reviewMachine = setup({
|
|
|
644
689
|
type: 'final',
|
|
645
690
|
},
|
|
646
691
|
},
|
|
647
|
-
output: () =>
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
692
|
+
output: ({ context }) => {
|
|
693
|
+
if (!isNonEmptyString(context.evaluatedRevision)) {
|
|
694
|
+
throw new Error('REVIEW reached done without a receipt-observed evaluated revision');
|
|
695
|
+
}
|
|
696
|
+
return {
|
|
697
|
+
noUnsettledFindings: true,
|
|
698
|
+
evaluatedRevision: context.evaluatedRevision,
|
|
699
|
+
};
|
|
700
|
+
},
|
|
651
701
|
});
|