@sublang/playbook 5.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.
- package/README.md +11 -7
- package/docs/cli.md +38 -35
- package/docs/configuration.md +58 -15
- package/docs/embedding.md +24 -16
- package/package.json +40 -21
- package/reference/sdlc/captain.playbook/captain.playbook.js +2 -0
- package/reference/sdlc/captain.playbook/captain.playbook.ts +2 -0
- package/reference/sdlc/code.md +55 -97
- package/reference/sdlc/code.playbook/code.fsm.d.ts +229 -94
- package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +26 -44
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +61 -66
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +100 -149
- package/reference/sdlc/code.playbook/code.fsm.js +587 -1347
- package/reference/sdlc/code.playbook/code.fsm.ts +809 -1650
- package/reference/sdlc/code.playbook/code.gears.md +51 -263
- package/reference/sdlc/code.playbook/code.playbook.d.ts +8 -47
- package/reference/sdlc/code.playbook/code.playbook.js +69 -656
- package/reference/sdlc/code.playbook/code.playbook.ts +90 -867
- package/reference/sdlc/code.playbook/code.registry.d.ts +9 -25
- package/reference/sdlc/code.playbook/code.registry.js +20 -78
- package/reference/sdlc/code.playbook/code.registry.ts +58 -122
- package/reference/sdlc/code.playbook/playbook-captain.js +93 -15
- package/reference/sdlc/code.playbook/playbook-captain.ts +115 -19
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +33 -22
- package/reference/sdlc/decide.md +54 -0
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +261 -0
- package/reference/sdlc/decide.playbook/decide.fsm.js +894 -0
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1152 -0
- package/reference/sdlc/decide.playbook/decide.gears.md +88 -0
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +67 -0
- package/reference/sdlc/{discuss.playbook/discuss.playbook.js → decide.playbook/decide.playbook.js} +471 -362
- package/reference/sdlc/{discuss.playbook/discuss.playbook.ts → decide.playbook/decide.playbook.ts} +575 -443
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +41 -0
- package/reference/sdlc/decide.playbook/decide.registry.js +60 -0
- package/reference/sdlc/decide.playbook/decide.registry.ts +125 -0
- package/reference/sdlc/review.md +81 -0
- package/reference/sdlc/review.playbook/review.fsm.d.ts +183 -0
- package/reference/sdlc/review.playbook/review.fsm.js +524 -0
- package/reference/sdlc/review.playbook/review.fsm.ts +652 -0
- package/reference/sdlc/review.playbook/review.gears.md +112 -0
- package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -0
- package/reference/sdlc/review.playbook/review.playbook.js +112 -0
- package/reference/sdlc/review.playbook/review.playbook.ts +201 -0
- package/reference/sdlc/review.playbook/review.registry.d.ts +43 -0
- package/reference/sdlc/review.playbook/review.registry.js +73 -0
- package/reference/sdlc/review.playbook/review.registry.ts +138 -0
- package/slc/gears2fsm.md +13 -4
- package/slc/link.md +48 -2
- package/slc/text2gears.md +22 -2
- package/src/runtime.d.ts +7 -0
- package/src/runtime.ts +12 -0
- package/src/xstate-playbook-runtime.d.ts +9 -2
- package/src/xstate-playbook-runtime.js +255 -21
- package/src/xstate-playbook-runtime.ts +333 -28
- package/src/xstate-runtime.js +25 -0
- package/src/xstate-runtime.ts +51 -0
- package/reference/sdlc/discuss.md +0 -93
- package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +0 -396
- package/reference/sdlc/discuss.playbook/discuss.fsm.js +0 -2067
- package/reference/sdlc/discuss.playbook/discuss.fsm.ts +0 -2465
- package/reference/sdlc/discuss.playbook/discuss.gears.md +0 -258
- package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +0 -113
- package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +0 -58
- package/reference/sdlc/discuss.playbook/discuss.registry.js +0 -97
- package/reference/sdlc/discuss.playbook/discuss.registry.ts +0 -153
|
@@ -1,1445 +1,685 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// so strict checking emits structural-mismatch noise without surfacing real
|
|
6
|
-
// bugs. The runner overrides `player` via `.provide({ actors: { ... } })`
|
|
7
|
-
// and re-asserts the public surface (`CodingContext`, `CodingEvent`,
|
|
8
|
-
// `PlayerInput`, `PlayerOutput`) at the boundary.
|
|
9
|
-
// @ts-nocheck
|
|
3
|
+
//
|
|
4
|
+
// Generated by slc/gears2fsm.md from ./code.gears.md.
|
|
10
5
|
import { assign, fromPromise, setup } from 'xstate';
|
|
11
|
-
const
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'reviewChangesMixed',
|
|
48
|
-
'reviewChangesAndChallengesSpecs',
|
|
49
|
-
'reviewChangesAndChallengesCode',
|
|
50
|
-
'reviewChangesAndChallengesMixed',
|
|
51
|
-
'adjudicateChallenges',
|
|
52
|
-
'commitCoderInitial',
|
|
53
|
-
'commitJoint',
|
|
54
|
-
];
|
|
55
|
-
const stateDescriptions = {
|
|
56
|
-
ready: 'Idle hub: waits for Boss to start or resume a coding sub-procedure.',
|
|
57
|
-
awaitBossReply: 'Waiting for Boss to answer a player question.',
|
|
58
|
-
planAndImplement: 'CODE-1: Coder assesses a Boss intent and either implements a single-commit change or drafts an IR.',
|
|
59
|
-
respondToReview: 'CODE-2: Coder addresses or challenges Reviewer findings.',
|
|
60
|
-
continueIr: 'CODE-3: Coder continues an IR after the previous task or IR draft passed review.',
|
|
61
|
-
summarizeSpecs: 'CODE-4: Coder summarizes a completed IR into minimal spec items.',
|
|
62
|
-
reviewBossCommitSpecs: 'CODE-5: Reviewer reviews a Boss-intent commit whose changes are only in spec item files.',
|
|
63
|
-
reviewBossCommitCode: 'CODE-6: Reviewer reviews a Boss-intent commit whose changes are only outside spec item files.',
|
|
64
|
-
reviewBossCommitMixed: 'CODE-7: Reviewer reviews a Boss-intent commit whose changes span both spec item files and other files.',
|
|
65
|
-
reviewIrTaskCommitSpecs: 'CODE-8: Reviewer reviews an IR-task commit whose changes are only in spec item files.',
|
|
66
|
-
reviewIrTaskCommitCode: 'CODE-9: Reviewer reviews an IR-task commit whose changes are only outside spec item files.',
|
|
67
|
-
reviewIrTaskCommitMixed: 'CODE-10: Reviewer reviews an IR-task commit whose changes span both spec item files and other files.',
|
|
68
|
-
reviewChangesSpecs: 'CODE-11: Reviewer reviews uncommitted Coder changes that touch only spec item files with no accompanying rebuttals.',
|
|
69
|
-
reviewChangesCode: 'CODE-12: Reviewer reviews uncommitted Coder changes that touch only files outside spec item files with no accompanying rebuttals.',
|
|
70
|
-
reviewChangesMixed: 'CODE-13: Reviewer reviews uncommitted Coder changes that touch both spec item files and other files with no accompanying rebuttals.',
|
|
71
|
-
reviewChangesAndChallengesSpecs: 'CODE-15: Reviewer reviews uncommitted Coder changes that touch only spec item files and adjudicates accompanying rebuttals in one round.',
|
|
72
|
-
reviewChangesAndChallengesCode: 'CODE-16: Reviewer reviews uncommitted Coder changes that touch only files outside spec item files and adjudicates accompanying rebuttals in one round.',
|
|
73
|
-
reviewChangesAndChallengesMixed: 'CODE-17: Reviewer reviews uncommitted Coder changes that touch both spec item files and other files and adjudicates accompanying rebuttals in one round.',
|
|
74
|
-
adjudicateChallenges: 'CODE-14: Reviewer adjudicates Coder rebuttals against the prior review when Coder produced no code edits this round.',
|
|
75
|
-
commitCoderInitial: 'CODE-18: Committer commits Coder Initial Changes when Reviewer has not played since the last commit.',
|
|
76
|
-
commitJoint: 'CODE-19: Committer commits changes when both Coder and Reviewer have played since the last commit.',
|
|
77
|
-
failed: 'Captures the last Captain error so the runner can report it. Boss may resume from here.',
|
|
78
|
-
done: 'The selected coding workflow has completed.',
|
|
6
|
+
const FIRST_PHASE_PROMPT = [
|
|
7
|
+
'> <caller-input>',
|
|
8
|
+
'> <run-results>',
|
|
9
|
+
'',
|
|
10
|
+
'Assess whether the coding intent can be completed well in one commit.',
|
|
11
|
+
'If yes, implement and test it, update the affected specs, and ensure @specs/map.md remains accurate.',
|
|
12
|
+
'Otherwise, decompose it into tasks sized to exactly one commit each, add a new IR under @specs/intents, and do not implement any IR task in this phase.',
|
|
13
|
+
'Plan affected spec updates before, with, or after their corresponding code changes, either as standalone IR tasks or as explicit work within related tasks.',
|
|
14
|
+
'',
|
|
15
|
+
'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
|
|
16
|
+
'',
|
|
17
|
+
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
18
|
+
"Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
|
|
19
|
+
'Make the commit message explain concisely what changed and why, including relevant verification.',
|
|
20
|
+
'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
|
|
21
|
+
'Coder is <coder-llm>; format the model token in conventional human form.',
|
|
22
|
+
].join('\n');
|
|
23
|
+
const IR_TASK_PROMPT = [
|
|
24
|
+
'> <ir-task>',
|
|
25
|
+
'> <run-results>',
|
|
26
|
+
'',
|
|
27
|
+
'Read IR-<#> and implement exactly the next unfinished task, including corresponding tests or specs if any.',
|
|
28
|
+
'Do not implement a later task in this phase.',
|
|
29
|
+
"Mark the IR's progress and deliverables when relevant.",
|
|
30
|
+
'If the IR will be finished after this phase, double-check that all acceptance criteria are met.',
|
|
31
|
+
'',
|
|
32
|
+
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
33
|
+
"Make the phase's minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
|
|
34
|
+
'Make the commit message explain concisely what changed and why, including relevant verification.',
|
|
35
|
+
'Report it as exactly one final-response line beginning `Commit: `, followed only by the exact commit identity.',
|
|
36
|
+
'Coder is <coder-llm>; format the model token in conventional human form.',
|
|
37
|
+
].join('\n');
|
|
38
|
+
const FIRST_PHASE_RESULTS = {
|
|
39
|
+
directCommit: 'Coder completed and committed the direct implementation phase. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.',
|
|
40
|
+
irCommit: 'Coder created and committed a new IR without implementing an IR task. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber`, and `irTask` naming the exact next unfinished task.',
|
|
41
|
+
needsBossReply: "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>`.",
|
|
79
42
|
};
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
const outputOf = (event) => event.output;
|
|
87
|
-
const guardIs = (guard) => ({ event }) => outputOf(event)?.guard === guard;
|
|
88
|
-
const guardAndOrigin = (guard, origin) => ({ context, event }) => outputOf(event)?.guard === guard && context.changeOrigin === origin;
|
|
89
|
-
const acceptedAfter = (subject) => ({ context, event }) => outputOf(event)?.guard === 'accepted' && context.reviewSubject === subject;
|
|
90
|
-
const noFindingsAfter = (afterReview) => ({ context, event }) => outputOf(event)?.guard === 'noFindings' &&
|
|
91
|
-
context.afterReview === afterReview;
|
|
92
|
-
const rememberCaptainOutput = assign({
|
|
93
|
-
lastResult: ({ event }) => outputOf(event),
|
|
94
|
-
lastError: () => undefined,
|
|
95
|
-
irNumber: ({ context, event }) => outputOf(event)?.irNumber ?? context.irNumber,
|
|
96
|
-
taskDescription: ({ context, event, }) => outputOf(event)?.taskDescription ?? context.taskDescription,
|
|
97
|
-
reviews: ({ context, event }) => outputOf(event)?.reviews ?? context.reviews,
|
|
98
|
-
challenges: ({ context, event, }) => outputOf(event)?.challenges ?? context.challenges,
|
|
99
|
-
});
|
|
100
|
-
const rememberCaptainError = assign({
|
|
101
|
-
lastError: ({ event }) => event.error,
|
|
102
|
-
});
|
|
103
|
-
const rememberEmptyBossReplyError = assign({
|
|
104
|
-
lastError: () => new Error('BOSS_REPLY received empty answer'),
|
|
105
|
-
});
|
|
106
|
-
const rememberBossInput = assign({
|
|
107
|
-
intent: ({ context, event }) => event.intent ?? context.intent,
|
|
108
|
-
irNumber: ({ context, event }) => event.irNumber ?? context.irNumber,
|
|
109
|
-
});
|
|
110
|
-
const needsBossReplyDescription = "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>`.";
|
|
111
|
-
const bossReplyInputFields = (context) => ({
|
|
112
|
-
pendingBossQuestion: context.pendingBossQuestion,
|
|
113
|
-
bossReply: context.bossReply,
|
|
114
|
-
});
|
|
115
|
-
const withNeedsBossReply = (result) => ({
|
|
116
|
-
...result,
|
|
117
|
-
needsBossReply: needsBossReplyDescription,
|
|
118
|
-
});
|
|
119
|
-
const captainStateMetadata = {
|
|
120
|
-
planAndImplement: { sourceItem: 'CODE-1', player: 'Coder' },
|
|
121
|
-
respondToReview: { sourceItem: 'CODE-2', player: 'Coder' },
|
|
122
|
-
continueIr: { sourceItem: 'CODE-3', player: 'Coder' },
|
|
123
|
-
summarizeSpecs: { sourceItem: 'CODE-4', player: 'Coder' },
|
|
124
|
-
reviewBossCommitSpecs: { sourceItem: 'CODE-5', player: 'Reviewer' },
|
|
125
|
-
reviewBossCommitCode: { sourceItem: 'CODE-6', player: 'Reviewer' },
|
|
126
|
-
reviewBossCommitMixed: { sourceItem: 'CODE-7', player: 'Reviewer' },
|
|
127
|
-
reviewIrTaskCommitSpecs: { sourceItem: 'CODE-8', player: 'Reviewer' },
|
|
128
|
-
reviewIrTaskCommitCode: { sourceItem: 'CODE-9', player: 'Reviewer' },
|
|
129
|
-
reviewIrTaskCommitMixed: { sourceItem: 'CODE-10', player: 'Reviewer' },
|
|
130
|
-
reviewChangesSpecs: { sourceItem: 'CODE-11', player: 'Reviewer' },
|
|
131
|
-
reviewChangesCode: { sourceItem: 'CODE-12', player: 'Reviewer' },
|
|
132
|
-
reviewChangesMixed: { sourceItem: 'CODE-13', player: 'Reviewer' },
|
|
133
|
-
reviewChangesAndChallengesSpecs: {
|
|
134
|
-
sourceItem: 'CODE-15',
|
|
135
|
-
player: 'Reviewer',
|
|
136
|
-
},
|
|
137
|
-
reviewChangesAndChallengesCode: { sourceItem: 'CODE-16', player: 'Reviewer' },
|
|
138
|
-
reviewChangesAndChallengesMixed: {
|
|
139
|
-
sourceItem: 'CODE-17',
|
|
140
|
-
player: 'Reviewer',
|
|
141
|
-
},
|
|
142
|
-
adjudicateChallenges: { sourceItem: 'CODE-14', player: 'Reviewer' },
|
|
143
|
-
commitCoderInitial: { sourceItem: 'CODE-18', player: 'Committer' },
|
|
144
|
-
commitJoint: { sourceItem: 'CODE-19', player: 'Committer' },
|
|
43
|
+
const IR_TASK_RESULTS = {
|
|
44
|
+
moreTasks: 'Coder completed and committed the current IR task and at least one task remains. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irTask` naming the exact next unfinished task.',
|
|
45
|
+
finalTask: 'Coder completed and committed the final IR task. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.',
|
|
46
|
+
needsBossReply: "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>`.",
|
|
145
47
|
};
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
'
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
48
|
+
const STATE_DESCRIPTIONS = {
|
|
49
|
+
ready: 'Waiting for a coding intent.',
|
|
50
|
+
runFirstPhase: 'Coder is implementing one direct phase or committing a new intent record.',
|
|
51
|
+
reviewFirstCommit: 'The REVIEW playbook is checking the first phase commit.',
|
|
52
|
+
runIrTask: 'Coder is implementing exactly one unfinished intent-record task.',
|
|
53
|
+
reviewIrTask: 'The REVIEW playbook is checking the latest task commit.',
|
|
54
|
+
awaitBossReply: 'Waiting for Boss to answer Coder.',
|
|
55
|
+
failed: 'The coding workflow failed and is waiting for a new coding intent.',
|
|
56
|
+
done: 'The coding workflow completed after REVIEW found no unsettled findings.',
|
|
57
|
+
};
|
|
58
|
+
function playbookMeta(stateId, player) {
|
|
59
|
+
return {
|
|
60
|
+
playbook: {
|
|
61
|
+
stateId,
|
|
62
|
+
description: STATE_DESCRIPTIONS[stateId],
|
|
63
|
+
...(player === undefined ? {} : { player }),
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function isRecord(value) {
|
|
68
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const prototype = Object.getPrototypeOf(value);
|
|
72
|
+
return prototype === Object.prototype || prototype === null;
|
|
73
|
+
}
|
|
74
|
+
function isNonEmptyString(value) {
|
|
75
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
76
|
+
}
|
|
77
|
+
function playerOutput(event) {
|
|
78
|
+
if (!isRecord(event))
|
|
79
|
+
return undefined;
|
|
80
|
+
return isRecord(event.output) ? event.output : undefined;
|
|
81
|
+
}
|
|
82
|
+
function outputGuard(event, guard) {
|
|
83
|
+
return playerOutput(event)?.guard === guard;
|
|
84
|
+
}
|
|
85
|
+
function outputString(event, field) {
|
|
86
|
+
const value = playerOutput(event)?.[field];
|
|
87
|
+
return isNonEmptyString(value) ? value : undefined;
|
|
88
|
+
}
|
|
89
|
+
function commitOutput(event) {
|
|
90
|
+
const coderOutput = outputString(event, 'coderOutput');
|
|
91
|
+
const latestCommit = outputString(event, 'latestCommit');
|
|
92
|
+
const commitLines = coderOutput
|
|
93
|
+
?.split('\n')
|
|
94
|
+
.filter((line) => line.startsWith('Commit: '));
|
|
95
|
+
if (coderOutput === undefined ||
|
|
96
|
+
latestCommit === undefined ||
|
|
97
|
+
commitLines?.length !== 1 ||
|
|
98
|
+
commitLines[0] !== `Commit: ${latestCommit}`) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return { coderOutput, latestCommit };
|
|
102
|
+
}
|
|
103
|
+
function isDirectCommit({ event }) {
|
|
104
|
+
return (outputGuard(event, 'directCommit') &&
|
|
105
|
+
commitOutput(event) !== undefined);
|
|
106
|
+
}
|
|
107
|
+
function isIrCommit({ event }) {
|
|
108
|
+
return (outputGuard(event, 'irCommit') &&
|
|
109
|
+
commitOutput(event) !== undefined &&
|
|
110
|
+
outputString(event, 'irNumber') !== undefined &&
|
|
111
|
+
outputString(event, 'irTask') !== undefined);
|
|
112
|
+
}
|
|
113
|
+
function isMoreTasks({ event }) {
|
|
114
|
+
return (outputGuard(event, 'moreTasks') &&
|
|
115
|
+
commitOutput(event) !== undefined &&
|
|
116
|
+
outputString(event, 'irTask') !== undefined);
|
|
117
|
+
}
|
|
118
|
+
function isFinalTask({ event }) {
|
|
119
|
+
return (outputGuard(event, 'finalTask') &&
|
|
120
|
+
commitOutput(event) !== undefined);
|
|
121
|
+
}
|
|
122
|
+
function needsBossReply({ event }) {
|
|
123
|
+
return (outputGuard(event, 'needsBossReply') &&
|
|
124
|
+
outputString(event, 'question') !== undefined);
|
|
125
|
+
}
|
|
126
|
+
function reviewOutput(event) {
|
|
127
|
+
return isRecord(event) ? event.output : undefined;
|
|
128
|
+
}
|
|
129
|
+
function isReviewApprovedValue(value) {
|
|
130
|
+
return (isRecord(value) &&
|
|
131
|
+
Object.keys(value).sort().join('\0') ===
|
|
132
|
+
['approvedCommit', 'noUnsettledFindings'].sort().join('\0') &&
|
|
133
|
+
value.approvedCommit === 'latest' &&
|
|
134
|
+
value.noUnsettledFindings === true);
|
|
135
|
+
}
|
|
136
|
+
function reviewApprovedFor(phase) {
|
|
137
|
+
return ({ context, event }) => context.phase === phase && isReviewApprovedValue(reviewOutput(event));
|
|
138
|
+
}
|
|
139
|
+
function isStateValue(value, ancestors = new Set()) {
|
|
140
|
+
if (typeof value === 'string')
|
|
141
|
+
return true;
|
|
142
|
+
if (!isRecord(value) || ancestors.has(value))
|
|
143
|
+
return false;
|
|
144
|
+
const next = new Set(ancestors).add(value);
|
|
145
|
+
return Reflect.ownKeys(value).every((key) => typeof key === 'string' &&
|
|
146
|
+
Object.prototype.propertyIsEnumerable.call(value, key) &&
|
|
147
|
+
isStateValue(value[key], next));
|
|
148
|
+
}
|
|
149
|
+
function isStringArray(value) {
|
|
150
|
+
return (Array.isArray(value) &&
|
|
151
|
+
Reflect.ownKeys(value).length === value.length + 1 &&
|
|
152
|
+
Reflect.ownKeys(value).every((key) => {
|
|
153
|
+
if (key === 'length')
|
|
154
|
+
return true;
|
|
155
|
+
return (typeof key === 'string' &&
|
|
156
|
+
/^(0|[1-9][0-9]*)$/.test(key) &&
|
|
157
|
+
Number(key) < value.length);
|
|
158
|
+
}) &&
|
|
159
|
+
value.every((entry) => isNonEmptyString(entry)) &&
|
|
160
|
+
new Set(value).size === value.length);
|
|
161
|
+
}
|
|
162
|
+
function isPlaybookState(value) {
|
|
163
|
+
if (!isRecord(value))
|
|
164
|
+
return false;
|
|
165
|
+
const allowed = new Set([
|
|
166
|
+
'value',
|
|
167
|
+
'activeStateIds',
|
|
168
|
+
'tags',
|
|
169
|
+
'status',
|
|
170
|
+
'quiescent',
|
|
171
|
+
'stateId',
|
|
172
|
+
]);
|
|
173
|
+
if (Reflect.ownKeys(value).some((key) => typeof key !== 'string' || !allowed.has(key)) ||
|
|
174
|
+
!Object.prototype.hasOwnProperty.call(value, 'value') ||
|
|
175
|
+
!Object.prototype.hasOwnProperty.call(value, 'activeStateIds') ||
|
|
176
|
+
!Object.prototype.hasOwnProperty.call(value, 'tags') ||
|
|
177
|
+
!Object.prototype.hasOwnProperty.call(value, 'status') ||
|
|
178
|
+
!Object.prototype.hasOwnProperty.call(value, 'quiescent')) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return (isStateValue(value.value) &&
|
|
182
|
+
isStringArray(value.activeStateIds) &&
|
|
183
|
+
isStringArray(value.tags) &&
|
|
184
|
+
(value.status === 'active' ||
|
|
185
|
+
value.status === 'done' ||
|
|
186
|
+
value.status === 'error' ||
|
|
187
|
+
value.status === 'stopped') &&
|
|
188
|
+
typeof value.quiescent === 'boolean' &&
|
|
189
|
+
(!Object.prototype.hasOwnProperty.call(value, 'stateId') ||
|
|
190
|
+
(isNonEmptyString(value.stateId) &&
|
|
191
|
+
value.activeStateIds.length === 1 &&
|
|
192
|
+
value.activeStateIds[0] === value.stateId)));
|
|
193
|
+
}
|
|
194
|
+
function nestedResultFromError(error) {
|
|
195
|
+
if (!(error instanceof Error))
|
|
196
|
+
return undefined;
|
|
197
|
+
const result = error.result;
|
|
198
|
+
return isRecord(result) ? result : undefined;
|
|
199
|
+
}
|
|
200
|
+
function normalizedReviewFailure(error) {
|
|
201
|
+
const result = nestedResultFromError(error);
|
|
202
|
+
if (result === undefined)
|
|
203
|
+
return undefined;
|
|
204
|
+
const allowed = new Set([
|
|
205
|
+
'status',
|
|
206
|
+
'playbookId',
|
|
207
|
+
'childSessionId',
|
|
208
|
+
'state',
|
|
209
|
+
'error',
|
|
210
|
+
]);
|
|
211
|
+
if (Reflect.ownKeys(result).some((key) => typeof key !== 'string' || !allowed.has(key)) ||
|
|
212
|
+
(result.status !== 'aborted' && result.status !== 'error') ||
|
|
213
|
+
result.playbookId !== 'review') {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
if (Object.prototype.hasOwnProperty.call(result, 'childSessionId') &&
|
|
217
|
+
!isNonEmptyString(result.childSessionId)) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
if (Object.prototype.hasOwnProperty.call(result, 'state') &&
|
|
221
|
+
!isPlaybookState(result.state)) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
let normalizedError;
|
|
225
|
+
if (Object.prototype.hasOwnProperty.call(result, 'error')) {
|
|
226
|
+
if (!isRecord(result.error))
|
|
227
|
+
return undefined;
|
|
228
|
+
const errorKeys = Reflect.ownKeys(result.error);
|
|
229
|
+
if (errorKeys.some((key) => typeof key !== 'string' ||
|
|
230
|
+
(key !== 'name' && key !== 'message' && key !== 'stack')) ||
|
|
231
|
+
!Object.prototype.hasOwnProperty.call(result.error, 'name') ||
|
|
232
|
+
!Object.prototype.hasOwnProperty.call(result.error, 'message') ||
|
|
233
|
+
!isNonEmptyString(result.error.name) ||
|
|
234
|
+
typeof result.error.message !== 'string' ||
|
|
235
|
+
(Object.prototype.hasOwnProperty.call(result.error, 'stack') &&
|
|
236
|
+
typeof result.error.stack !== 'string')) {
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
normalizedError = {
|
|
240
|
+
name: result.error.name,
|
|
241
|
+
message: result.error.message,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
else if (result.status === 'error') {
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
status: result.status,
|
|
249
|
+
...(normalizedError === undefined ? {} : { error: normalizedError }),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function authoredReviewFailure({ event }) {
|
|
253
|
+
const error = isRecord(event) ? event.error : undefined;
|
|
254
|
+
return normalizedReviewFailure(error) !== undefined;
|
|
255
|
+
}
|
|
256
|
+
function compactError(value) {
|
|
257
|
+
if (value instanceof Error) {
|
|
209
258
|
return {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
sourceItem: input.sourceItem,
|
|
213
|
-
player: input.player,
|
|
214
|
-
question: outputOf(event)?.question ?? '',
|
|
259
|
+
name: value.name || 'Error',
|
|
260
|
+
message: value.message,
|
|
215
261
|
};
|
|
262
|
+
}
|
|
263
|
+
return { name: 'Error', message: String(value) };
|
|
264
|
+
}
|
|
265
|
+
function authoredReviewError(event) {
|
|
266
|
+
const outer = isRecord(event) ? event.error : undefined;
|
|
267
|
+
const failure = normalizedReviewFailure(outer);
|
|
268
|
+
if (failure?.error !== undefined)
|
|
269
|
+
return failure.error;
|
|
270
|
+
if (failure?.status === 'aborted') {
|
|
271
|
+
return { name: 'AbortError', message: 'REVIEW was aborted.' };
|
|
272
|
+
}
|
|
273
|
+
return compactError(outer);
|
|
274
|
+
}
|
|
275
|
+
function quoteRelay(label, value) {
|
|
276
|
+
const lines = value.split('\n');
|
|
277
|
+
return [`> ${label}: ${lines[0] ?? ''}`, ...lines.slice(1).map((line) => `> ${line}`)].join('\n');
|
|
278
|
+
}
|
|
279
|
+
function initialReviewText(context) {
|
|
280
|
+
return [
|
|
281
|
+
quoteRelay('Initial intent', context.callerInput ?? ''),
|
|
282
|
+
quoteRelay('Coder output', context.coderOutput ?? ''),
|
|
283
|
+
].join('\n');
|
|
284
|
+
}
|
|
285
|
+
function irTaskReviewText(context) {
|
|
286
|
+
return [
|
|
287
|
+
quoteRelay('IR task', context.irTask ?? ''),
|
|
288
|
+
quoteRelay('Coder output', context.coderOutput ?? ''),
|
|
289
|
+
].join('\n');
|
|
290
|
+
}
|
|
291
|
+
const machineSetup = setup({
|
|
292
|
+
types: {},
|
|
293
|
+
actors: {
|
|
294
|
+
player: fromPromise(async () => {
|
|
295
|
+
throw new Error('player actor must be provided by the runner');
|
|
296
|
+
}),
|
|
297
|
+
playbook: fromPromise(async () => {
|
|
298
|
+
throw new Error('playbook actor must be provided by the runner');
|
|
299
|
+
}),
|
|
216
300
|
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const resumableStates = (ids) => ids.map((id) => ({
|
|
238
|
-
target: `#${id}`,
|
|
239
|
-
guard: ({ context, event, }) => event.type === 'BOSS_REPLY' &&
|
|
240
|
-
context.pendingBossQuestion?.resumeStateId === id &&
|
|
241
|
-
(event.questionId === undefined ||
|
|
242
|
-
event.questionId === context.pendingBossQuestion.questionId),
|
|
243
|
-
reenter: true,
|
|
244
|
-
actions: rememberBossReply,
|
|
245
|
-
}));
|
|
246
|
-
const bossReplyIsEmpty = ({ event }) => event.type === 'BOSS_REPLY' && event.answer.trim() === '';
|
|
247
|
-
const withNeedsBossReplyTransition = (resumeStateId, transitions) => [
|
|
248
|
-
...transitions.map((transition) => withClearBossReplyContext(transition)),
|
|
249
|
-
{
|
|
250
|
-
guard: guardIs('needsBossReply'),
|
|
251
|
-
target: '#awaitBossReply',
|
|
252
|
-
actions: [rememberCaptainOutput, setPendingBossQuestion(resumeStateId)],
|
|
253
|
-
},
|
|
254
|
-
];
|
|
255
|
-
const captainError = {
|
|
256
|
-
target: '#failed',
|
|
257
|
-
actions: rememberCaptainError,
|
|
258
|
-
};
|
|
259
|
-
const readyEvents = {
|
|
260
|
-
START_CODING: {
|
|
261
|
-
target: '#planAndImplement',
|
|
262
|
-
actions: [
|
|
263
|
-
rememberBossInput,
|
|
264
|
-
assign({
|
|
265
|
-
workflow: () => 'singleCommit',
|
|
266
|
-
changeOrigin: () => 'bossIntent',
|
|
267
|
-
afterReview: () => 'done',
|
|
268
|
-
}),
|
|
269
|
-
],
|
|
270
|
-
},
|
|
271
|
-
CONTINUE_IR: {
|
|
272
|
-
target: '#continueIr',
|
|
273
|
-
actions: [
|
|
274
|
-
rememberBossInput,
|
|
275
|
-
assign({
|
|
276
|
-
workflow: () => 'iteration',
|
|
277
|
-
changeOrigin: () => 'irTask',
|
|
278
|
-
afterReview: () => 'continueIr',
|
|
279
|
-
}),
|
|
280
|
-
],
|
|
301
|
+
guards: {
|
|
302
|
+
isDirectCommit,
|
|
303
|
+
isIrCommit,
|
|
304
|
+
isMoreTasks,
|
|
305
|
+
isFinalTask,
|
|
306
|
+
needsBossReply,
|
|
307
|
+
reviewApprovedDirect: reviewApprovedFor('direct'),
|
|
308
|
+
reviewApprovedIrCreated: reviewApprovedFor('ir-created'),
|
|
309
|
+
reviewApprovedMoreTasks: reviewApprovedFor('ir-task-more'),
|
|
310
|
+
reviewApprovedFinalTask: reviewApprovedFor('ir-task-final'),
|
|
311
|
+
authoredReviewFailure,
|
|
312
|
+
emptyBossReply: ({ event }) => event.type === 'BOSS_REPLY' && event.answer.trim().length === 0,
|
|
313
|
+
resumesFirstPhase: ({ context, event }) => event.type === 'BOSS_REPLY' &&
|
|
314
|
+
event.answer.trim().length > 0 &&
|
|
315
|
+
context.pendingBossQuestion?.resumeStateId === 'runFirstPhase' &&
|
|
316
|
+
(event.questionId === undefined || event.questionId === 'runFirstPhase'),
|
|
317
|
+
resumesIrTask: ({ context, event }) => event.type === 'BOSS_REPLY' &&
|
|
318
|
+
event.answer.trim().length > 0 &&
|
|
319
|
+
context.pendingBossQuestion?.resumeStateId === 'runIrTask' &&
|
|
320
|
+
(event.questionId === undefined || event.questionId === 'runIrTask'),
|
|
281
321
|
},
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
322
|
+
actions: {
|
|
323
|
+
startCoding: assign(({ context, event }) => {
|
|
324
|
+
if (event.type !== 'START_CODE')
|
|
325
|
+
return {};
|
|
326
|
+
return {
|
|
327
|
+
coderPlayer: context.coderPlayer,
|
|
328
|
+
runResults: context.runResults,
|
|
329
|
+
callerInput: event.callerInput,
|
|
330
|
+
coderOutput: undefined,
|
|
331
|
+
latestCommit: undefined,
|
|
332
|
+
irNumber: undefined,
|
|
333
|
+
irTask: undefined,
|
|
334
|
+
nextIrTask: undefined,
|
|
335
|
+
phase: undefined,
|
|
336
|
+
completion: undefined,
|
|
337
|
+
reviewError: undefined,
|
|
338
|
+
lastError: undefined,
|
|
339
|
+
pendingBossQuestion: undefined,
|
|
340
|
+
bossReply: undefined,
|
|
341
|
+
};
|
|
342
|
+
}),
|
|
343
|
+
rememberDirectCommit: assign(({ event }) => ({
|
|
344
|
+
coderOutput: outputString(event, 'coderOutput'),
|
|
345
|
+
latestCommit: outputString(event, 'latestCommit'),
|
|
346
|
+
phase: 'direct',
|
|
347
|
+
pendingBossQuestion: undefined,
|
|
348
|
+
bossReply: undefined,
|
|
349
|
+
})),
|
|
350
|
+
rememberIrCommit: assign(({ event }) => ({
|
|
351
|
+
coderOutput: outputString(event, 'coderOutput'),
|
|
352
|
+
latestCommit: outputString(event, 'latestCommit'),
|
|
353
|
+
irNumber: outputString(event, 'irNumber'),
|
|
354
|
+
irTask: outputString(event, 'irTask'),
|
|
355
|
+
nextIrTask: undefined,
|
|
356
|
+
phase: 'ir-created',
|
|
357
|
+
pendingBossQuestion: undefined,
|
|
358
|
+
bossReply: undefined,
|
|
359
|
+
})),
|
|
360
|
+
rememberMoreTasks: assign(({ event }) => ({
|
|
361
|
+
coderOutput: outputString(event, 'coderOutput'),
|
|
362
|
+
latestCommit: outputString(event, 'latestCommit'),
|
|
363
|
+
nextIrTask: outputString(event, 'irTask'),
|
|
364
|
+
phase: 'ir-task-more',
|
|
365
|
+
pendingBossQuestion: undefined,
|
|
366
|
+
bossReply: undefined,
|
|
367
|
+
})),
|
|
368
|
+
rememberFinalTask: assign(({ event }) => ({
|
|
369
|
+
coderOutput: outputString(event, 'coderOutput'),
|
|
370
|
+
latestCommit: outputString(event, 'latestCommit'),
|
|
371
|
+
nextIrTask: undefined,
|
|
372
|
+
phase: 'ir-task-final',
|
|
373
|
+
pendingBossQuestion: undefined,
|
|
374
|
+
bossReply: undefined,
|
|
375
|
+
})),
|
|
376
|
+
advanceToNextIrTask: assign(({ context }) => ({
|
|
377
|
+
irTask: context.nextIrTask,
|
|
378
|
+
nextIrTask: undefined,
|
|
379
|
+
})),
|
|
380
|
+
rememberPendingQuestion: assign(({ context, event }) => {
|
|
381
|
+
const question = outputString(event, 'question');
|
|
382
|
+
const output = playerOutput(event);
|
|
383
|
+
const stateId = context.irNumber === undefined ? 'runFirstPhase' : 'runIrTask';
|
|
384
|
+
if (question === undefined || output?.guard !== 'needsBossReply') {
|
|
385
|
+
return { lastError: new Error('Coder question output was malformed') };
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
pendingBossQuestion: {
|
|
389
|
+
questionId: stateId,
|
|
390
|
+
resumeStateId: stateId,
|
|
391
|
+
sourceItem: stateId === 'runFirstPhase' ? 'CODE-1' : 'CODE-3',
|
|
392
|
+
player: 'Coder',
|
|
393
|
+
question,
|
|
394
|
+
},
|
|
395
|
+
bossReply: undefined,
|
|
396
|
+
};
|
|
397
|
+
}),
|
|
398
|
+
rememberBossReply: assign(({ event }) => event.type === 'BOSS_REPLY' ? { bossReply: event.answer } : {}),
|
|
399
|
+
rememberEmptyBossReplyError: assign({
|
|
400
|
+
lastError: () => new Error('BOSS_REPLY received an empty answer'),
|
|
401
|
+
pendingBossQuestion: undefined,
|
|
402
|
+
bossReply: undefined,
|
|
403
|
+
}),
|
|
404
|
+
completeSuccessfully: assign({
|
|
405
|
+
completion: 'complete',
|
|
406
|
+
reviewError: undefined,
|
|
407
|
+
}),
|
|
408
|
+
completeWithReviewFailure: assign(({ event }) => ({
|
|
409
|
+
completion: 'review-failed',
|
|
410
|
+
reviewError: authoredReviewError(event),
|
|
411
|
+
})),
|
|
412
|
+
completeWithInvalidReviewOutput: assign(({ event }) => ({
|
|
413
|
+
completion: 'review-failed',
|
|
414
|
+
reviewError: {
|
|
415
|
+
name: 'ReviewContractError',
|
|
416
|
+
message: `REVIEW returned an invalid approval result: ` +
|
|
417
|
+
JSON.stringify(reviewOutput(event)),
|
|
418
|
+
},
|
|
419
|
+
})),
|
|
420
|
+
rememberActorError: assign(({ event }) => ({
|
|
421
|
+
lastError: event.error,
|
|
422
|
+
})),
|
|
423
|
+
rememberMalformedPlayerOutput: assign(({ context }) => ({
|
|
424
|
+
lastError: new Error(`Coder result for ${context.irNumber === undefined ? 'CODE-1' : 'CODE-3'} ` +
|
|
425
|
+
'did not match a declared outcome.'),
|
|
426
|
+
})),
|
|
292
427
|
},
|
|
293
|
-
};
|
|
294
|
-
const awaitBossReplyEvents = {
|
|
295
|
-
START_CODING: withClearBossReplyContext(readyEvents.START_CODING),
|
|
296
|
-
CONTINUE_IR: withClearBossReplyContext(readyEvents.CONTINUE_IR),
|
|
297
|
-
SUMMARIZE_IR: withClearBossReplyContext(readyEvents.SUMMARIZE_IR),
|
|
298
|
-
BOSS_INTERRUPT: bossInterrupts(jumpableStateIds, [clearBossReplyContext]),
|
|
299
|
-
BOSS_REPLY: [
|
|
300
|
-
{
|
|
301
|
-
guard: bossReplyIsEmpty,
|
|
302
|
-
target: '#failed',
|
|
303
|
-
actions: [clearBossReplyContext, rememberEmptyBossReplyError],
|
|
304
|
-
},
|
|
305
|
-
...resumableStates(resumableStateIds),
|
|
306
|
-
],
|
|
307
|
-
};
|
|
308
|
-
const playerPlaceholder = fromPromise(async () => {
|
|
309
|
-
throw new Error('player actor must be provided by the runner');
|
|
310
428
|
});
|
|
311
|
-
export const codingMachine =
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
player: playerPlaceholder,
|
|
315
|
-
},
|
|
316
|
-
}).createMachine({
|
|
317
|
-
id: 'coding',
|
|
429
|
+
export const codingMachine = machineSetup.createMachine({
|
|
430
|
+
id: 'code',
|
|
431
|
+
initial: 'ready',
|
|
318
432
|
context: ({ input }) => ({
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
coderPlayer: input?.coderPlayer,
|
|
322
|
-
reviewerPlayer: input?.reviewerPlayer,
|
|
323
|
-
committerPlayer: input?.committerPlayer,
|
|
433
|
+
coderPlayer: isNonEmptyString(input.coderPlayer) ? input.coderPlayer : 'Coder',
|
|
434
|
+
runResults: typeof input.runResults === 'string' ? input.runResults : '',
|
|
324
435
|
}),
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
436
|
+
output: ({ context }) => {
|
|
437
|
+
const lastCodeCommit = context.latestCommit;
|
|
438
|
+
if (!isNonEmptyString(lastCodeCommit)) {
|
|
439
|
+
throw new Error('CODE reached a final state without a commit identity');
|
|
440
|
+
}
|
|
441
|
+
const lastCodeOutput = context.coderOutput ?? '';
|
|
442
|
+
if (context.completion === 'review-failed') {
|
|
443
|
+
return {
|
|
444
|
+
status: 'review-failed',
|
|
445
|
+
lastCodeCommit,
|
|
446
|
+
lastCodeOutput,
|
|
447
|
+
error: context.reviewError ?? {
|
|
448
|
+
name: 'Error',
|
|
449
|
+
message: 'REVIEW failed without error detail.',
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return { status: 'complete', lastCodeCommit, lastCodeOutput };
|
|
328
454
|
},
|
|
329
455
|
states: {
|
|
330
456
|
ready: {
|
|
331
457
|
id: 'ready',
|
|
332
|
-
description:
|
|
458
|
+
description: STATE_DESCRIPTIONS.ready,
|
|
333
459
|
meta: playbookMeta('ready'),
|
|
334
460
|
tags: ['playbook.parked'],
|
|
335
|
-
on:
|
|
336
|
-
|
|
337
|
-
awaitBossReply: {
|
|
338
|
-
id: 'awaitBossReply',
|
|
339
|
-
description: stateDescriptions.awaitBossReply,
|
|
340
|
-
meta: playbookMeta('awaitBossReply'),
|
|
341
|
-
tags: ['playbook.parked'],
|
|
342
|
-
on: awaitBossReplyEvents,
|
|
343
|
-
},
|
|
344
|
-
planAndImplement: {
|
|
345
|
-
id: 'planAndImplement',
|
|
346
|
-
description: stateDescriptions.planAndImplement,
|
|
347
|
-
meta: playbookMeta('planAndImplement'),
|
|
348
|
-
tags: ['playbook.busy'],
|
|
349
|
-
invoke: {
|
|
350
|
-
src: 'player',
|
|
351
|
-
input: ({ context }) => planAndImplementInput(context),
|
|
352
|
-
onDone: withNeedsBossReplyTransition('planAndImplement', [
|
|
353
|
-
{
|
|
354
|
-
guard: guardIs('singleCommitReady'),
|
|
355
|
-
target: '#commitCoderInitial',
|
|
356
|
-
actions: [
|
|
357
|
-
rememberCaptainOutput,
|
|
358
|
-
assign({
|
|
359
|
-
workflow: () => 'singleCommit',
|
|
360
|
-
changeOrigin: () => 'bossIntent',
|
|
361
|
-
afterReview: () => 'done',
|
|
362
|
-
}),
|
|
363
|
-
],
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
guard: guardIs('irDrafted'),
|
|
367
|
-
target: '#commitCoderInitial',
|
|
368
|
-
actions: [
|
|
369
|
-
rememberCaptainOutput,
|
|
370
|
-
assign({
|
|
371
|
-
workflow: () => 'iteration',
|
|
372
|
-
changeOrigin: () => 'bossIntent',
|
|
373
|
-
afterReview: () => 'continueIr',
|
|
374
|
-
}),
|
|
375
|
-
],
|
|
376
|
-
},
|
|
377
|
-
]),
|
|
378
|
-
onError: captainError,
|
|
461
|
+
on: {
|
|
462
|
+
START_CODE: { target: 'runFirstPhase', actions: 'startCoding' },
|
|
379
463
|
},
|
|
380
464
|
},
|
|
381
|
-
|
|
382
|
-
id: '
|
|
383
|
-
description:
|
|
384
|
-
meta: playbookMeta('
|
|
465
|
+
runFirstPhase: {
|
|
466
|
+
id: 'runFirstPhase',
|
|
467
|
+
description: STATE_DESCRIPTIONS.runFirstPhase,
|
|
468
|
+
meta: playbookMeta('runFirstPhase', 'Coder'),
|
|
385
469
|
tags: ['playbook.busy'],
|
|
386
470
|
invoke: {
|
|
387
471
|
src: 'player',
|
|
388
472
|
input: ({ context }) => ({
|
|
389
|
-
stateId: '
|
|
473
|
+
stateId: 'runFirstPhase',
|
|
390
474
|
player: 'Coder',
|
|
391
|
-
sourceItem: 'CODE-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
changesMadeCodeAndChallenged: 'Coder produced unstaged/untracked edits only outside spec item files AND challenged one or more review items. Output shall include `challenges: <numbered rebuttals, one per challenged item>`.',
|
|
404
|
-
changesMadeMixedAndChallenged: 'Coder produced unstaged/untracked edits spanning both spec item files and other files AND challenged one or more review items. Output shall include `challenges: <numbered rebuttals, one per challenged item>`.',
|
|
405
|
-
challengesRaised: 'Coder challenged one or more review items without producing any code edits. Output shall include `challenges: <numbered rebuttals, one per challenged item>`.',
|
|
406
|
-
accepted: 'Coder accepted the review outcome without further edits.',
|
|
407
|
-
needsBossReply: needsBossReplyDescription,
|
|
408
|
-
},
|
|
409
|
-
}),
|
|
410
|
-
onDone: withNeedsBossReplyTransition('respondToReview', [
|
|
411
|
-
{
|
|
412
|
-
guard: guardIs('changesMadeSpecs'),
|
|
413
|
-
target: '#reviewChangesSpecs',
|
|
414
|
-
actions: rememberCaptainOutput,
|
|
415
|
-
},
|
|
416
|
-
{
|
|
417
|
-
guard: guardIs('changesMadeCode'),
|
|
418
|
-
target: '#reviewChangesCode',
|
|
419
|
-
actions: rememberCaptainOutput,
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
guard: guardIs('changesMadeMixed'),
|
|
423
|
-
target: '#reviewChangesMixed',
|
|
424
|
-
actions: rememberCaptainOutput,
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
guard: guardIs('changesMadeSpecsAndChallenged'),
|
|
428
|
-
target: '#reviewChangesAndChallengesSpecs',
|
|
429
|
-
actions: rememberCaptainOutput,
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
guard: guardIs('changesMadeCodeAndChallenged'),
|
|
433
|
-
target: '#reviewChangesAndChallengesCode',
|
|
434
|
-
actions: rememberCaptainOutput,
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
guard: guardIs('changesMadeMixedAndChallenged'),
|
|
438
|
-
target: '#reviewChangesAndChallengesMixed',
|
|
439
|
-
actions: rememberCaptainOutput,
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
guard: guardIs('challengesRaised'),
|
|
443
|
-
target: '#adjudicateChallenges',
|
|
444
|
-
actions: rememberCaptainOutput,
|
|
445
|
-
},
|
|
446
|
-
{
|
|
447
|
-
guard: acceptedAfter('changes'),
|
|
448
|
-
target: '#commitJoint',
|
|
449
|
-
actions: rememberCaptainOutput,
|
|
450
|
-
},
|
|
451
|
-
{
|
|
452
|
-
guard: ({ context, event }) => outputOf(event)?.guard === 'accepted' &&
|
|
453
|
-
context.reviewSubject === 'commit' &&
|
|
454
|
-
context.afterReview === 'continueIr',
|
|
455
|
-
target: '#continueIr',
|
|
456
|
-
actions: rememberCaptainOutput,
|
|
457
|
-
},
|
|
458
|
-
{
|
|
459
|
-
guard: ({ context, event }) => outputOf(event)?.guard === 'accepted' &&
|
|
460
|
-
context.reviewSubject === 'commit' &&
|
|
461
|
-
context.afterReview === 'summarizeSpecs',
|
|
462
|
-
target: '#summarizeSpecs',
|
|
463
|
-
actions: rememberCaptainOutput,
|
|
464
|
-
},
|
|
465
|
-
{
|
|
466
|
-
guard: ({ context, event }) => outputOf(event)?.guard === 'accepted' &&
|
|
467
|
-
context.reviewSubject === 'commit' &&
|
|
468
|
-
context.afterReview === 'done',
|
|
469
|
-
target: '#done',
|
|
470
|
-
actions: rememberCaptainOutput,
|
|
471
|
-
},
|
|
472
|
-
]),
|
|
473
|
-
onError: captainError,
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
continueIr: {
|
|
477
|
-
id: 'continueIr',
|
|
478
|
-
description: stateDescriptions.continueIr,
|
|
479
|
-
meta: playbookMeta('continueIr'),
|
|
480
|
-
tags: ['playbook.busy'],
|
|
481
|
-
invoke: {
|
|
482
|
-
src: 'player',
|
|
483
|
-
input: ({ context }) => continueIrInput(context),
|
|
484
|
-
onDone: withNeedsBossReplyTransition('continueIr', [
|
|
485
|
-
{
|
|
486
|
-
guard: guardIs('taskReady'),
|
|
487
|
-
target: '#commitCoderInitial',
|
|
488
|
-
actions: [
|
|
489
|
-
rememberCaptainOutput,
|
|
490
|
-
assign({
|
|
491
|
-
workflow: () => 'iteration',
|
|
492
|
-
changeOrigin: () => 'irTask',
|
|
493
|
-
afterReview: () => 'continueIr',
|
|
494
|
-
}),
|
|
495
|
-
],
|
|
496
|
-
},
|
|
497
|
-
{
|
|
498
|
-
guard: guardIs('iterationDone'),
|
|
499
|
-
target: '#summarizeSpecs',
|
|
500
|
-
actions: [
|
|
501
|
-
rememberCaptainOutput,
|
|
502
|
-
assign({
|
|
503
|
-
workflow: () => 'specSummary',
|
|
504
|
-
afterReview: () => 'done',
|
|
505
|
-
}),
|
|
506
|
-
],
|
|
507
|
-
},
|
|
508
|
-
]),
|
|
509
|
-
onError: captainError,
|
|
510
|
-
},
|
|
511
|
-
},
|
|
512
|
-
summarizeSpecs: {
|
|
513
|
-
id: 'summarizeSpecs',
|
|
514
|
-
description: stateDescriptions.summarizeSpecs,
|
|
515
|
-
meta: playbookMeta('summarizeSpecs'),
|
|
516
|
-
tags: ['playbook.busy'],
|
|
517
|
-
invoke: {
|
|
518
|
-
src: 'player',
|
|
519
|
-
input: ({ context }) => summarizeSpecsInput(context),
|
|
520
|
-
onDone: withNeedsBossReplyTransition('summarizeSpecs', [
|
|
521
|
-
{
|
|
522
|
-
guard: guardIs('specsReady'),
|
|
523
|
-
target: '#commitCoderInitial',
|
|
524
|
-
actions: [
|
|
525
|
-
rememberCaptainOutput,
|
|
526
|
-
assign({
|
|
527
|
-
workflow: () => 'specSummary',
|
|
528
|
-
changeOrigin: () => 'irTask',
|
|
529
|
-
afterReview: () => 'done',
|
|
530
|
-
}),
|
|
531
|
-
],
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
guard: guardIs('noSpecChanges'),
|
|
535
|
-
target: '#done',
|
|
536
|
-
actions: rememberCaptainOutput,
|
|
537
|
-
},
|
|
538
|
-
]),
|
|
539
|
-
onError: captainError,
|
|
540
|
-
},
|
|
541
|
-
},
|
|
542
|
-
reviewBossCommitSpecs: {
|
|
543
|
-
id: 'reviewBossCommitSpecs',
|
|
544
|
-
description: stateDescriptions.reviewBossCommitSpecs,
|
|
545
|
-
meta: playbookMeta('reviewBossCommitSpecs'),
|
|
546
|
-
tags: ['playbook.busy'],
|
|
547
|
-
invoke: {
|
|
548
|
-
src: 'player',
|
|
549
|
-
input: ({ context }) => ({
|
|
550
|
-
stateId: 'reviewBossCommitSpecs',
|
|
551
|
-
player: 'Reviewer',
|
|
552
|
-
sourceItem: 'CODE-5',
|
|
553
|
-
...bossReplyInputFields(context),
|
|
554
|
-
intent: context.intent,
|
|
555
|
-
prompt: [
|
|
556
|
-
'Review the latest commit.',
|
|
557
|
-
'Refer to the commit message.',
|
|
558
|
-
'Verify any affected spec items are:',
|
|
559
|
-
'',
|
|
560
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
561
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
562
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
563
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
564
|
-
'',
|
|
565
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
566
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
567
|
-
'Verify @specs/map.md reflects the changes.',
|
|
568
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
569
|
-
'Do not edit files or commit; report findings only.',
|
|
570
|
-
].join('\n'),
|
|
571
|
-
result: {
|
|
572
|
-
noFindings: 'The spec-only commit has no review findings.',
|
|
573
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
574
|
-
needsBossReply: needsBossReplyDescription,
|
|
575
|
-
},
|
|
576
|
-
}),
|
|
577
|
-
onDone: withNeedsBossReplyTransition('reviewBossCommitSpecs', [
|
|
578
|
-
{
|
|
579
|
-
guard: noFindingsAfter('continueIr'),
|
|
580
|
-
target: '#continueIr',
|
|
581
|
-
actions: rememberCaptainOutput,
|
|
582
|
-
},
|
|
583
|
-
{
|
|
584
|
-
guard: noFindingsAfter('summarizeSpecs'),
|
|
585
|
-
target: '#summarizeSpecs',
|
|
586
|
-
actions: rememberCaptainOutput,
|
|
587
|
-
},
|
|
588
|
-
{
|
|
589
|
-
guard: noFindingsAfter('done'),
|
|
590
|
-
target: '#done',
|
|
591
|
-
actions: rememberCaptainOutput,
|
|
592
|
-
},
|
|
593
|
-
{
|
|
594
|
-
guard: guardIs('hasFindings'),
|
|
595
|
-
target: '#respondToReview',
|
|
596
|
-
actions: [
|
|
597
|
-
rememberCaptainOutput,
|
|
598
|
-
assign({ reviewSubject: () => 'commit' }),
|
|
599
|
-
],
|
|
600
|
-
},
|
|
601
|
-
]),
|
|
602
|
-
onError: captainError,
|
|
603
|
-
},
|
|
604
|
-
},
|
|
605
|
-
reviewBossCommitCode: {
|
|
606
|
-
id: 'reviewBossCommitCode',
|
|
607
|
-
description: stateDescriptions.reviewBossCommitCode,
|
|
608
|
-
meta: playbookMeta('reviewBossCommitCode'),
|
|
609
|
-
tags: ['playbook.busy'],
|
|
610
|
-
invoke: {
|
|
611
|
-
src: 'player',
|
|
612
|
-
input: ({ context }) => ({
|
|
613
|
-
stateId: 'reviewBossCommitCode',
|
|
614
|
-
player: 'Reviewer',
|
|
615
|
-
sourceItem: 'CODE-6',
|
|
616
|
-
...bossReplyInputFields(context),
|
|
617
|
-
intent: context.intent,
|
|
618
|
-
prompt: [
|
|
619
|
-
'Review the latest commit.',
|
|
620
|
-
'Refer to the commit message.',
|
|
621
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
622
|
-
"Think thoroughly — don't just approve or reject.",
|
|
623
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
624
|
-
'Verify @specs/map.md reflects the changes.',
|
|
625
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
626
|
-
'Do not edit files or commit; report findings only.',
|
|
627
|
-
].join('\n'),
|
|
628
|
-
result: {
|
|
629
|
-
noFindings: 'The code-only commit has no review findings.',
|
|
630
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
631
|
-
needsBossReply: needsBossReplyDescription,
|
|
632
|
-
},
|
|
633
|
-
}),
|
|
634
|
-
onDone: withNeedsBossReplyTransition('reviewBossCommitCode', [
|
|
635
|
-
{
|
|
636
|
-
guard: noFindingsAfter('continueIr'),
|
|
637
|
-
target: '#continueIr',
|
|
638
|
-
actions: rememberCaptainOutput,
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
guard: noFindingsAfter('summarizeSpecs'),
|
|
642
|
-
target: '#summarizeSpecs',
|
|
643
|
-
actions: rememberCaptainOutput,
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
guard: noFindingsAfter('done'),
|
|
647
|
-
target: '#done',
|
|
648
|
-
actions: rememberCaptainOutput,
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
guard: guardIs('hasFindings'),
|
|
652
|
-
target: '#respondToReview',
|
|
653
|
-
actions: [
|
|
654
|
-
rememberCaptainOutput,
|
|
655
|
-
assign({ reviewSubject: () => 'commit' }),
|
|
656
|
-
],
|
|
657
|
-
},
|
|
658
|
-
]),
|
|
659
|
-
onError: captainError,
|
|
660
|
-
},
|
|
661
|
-
},
|
|
662
|
-
reviewBossCommitMixed: {
|
|
663
|
-
id: 'reviewBossCommitMixed',
|
|
664
|
-
description: stateDescriptions.reviewBossCommitMixed,
|
|
665
|
-
meta: playbookMeta('reviewBossCommitMixed'),
|
|
666
|
-
tags: ['playbook.busy'],
|
|
667
|
-
invoke: {
|
|
668
|
-
src: 'player',
|
|
669
|
-
input: ({ context }) => ({
|
|
670
|
-
stateId: 'reviewBossCommitMixed',
|
|
671
|
-
player: 'Reviewer',
|
|
672
|
-
sourceItem: 'CODE-7',
|
|
673
|
-
...bossReplyInputFields(context),
|
|
674
|
-
intent: context.intent,
|
|
675
|
-
prompt: [
|
|
676
|
-
'Review the latest commit.',
|
|
677
|
-
'Refer to the commit message.',
|
|
678
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
679
|
-
"Think thoroughly — don't just approve or reject.",
|
|
680
|
-
'Verify any affected spec items are:',
|
|
681
|
-
'',
|
|
682
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
683
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
684
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
685
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
686
|
-
'',
|
|
687
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
688
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
689
|
-
'Verify @specs/map.md reflects the changes.',
|
|
690
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
691
|
-
'Do not edit files or commit; report findings only.',
|
|
692
|
-
].join('\n'),
|
|
693
|
-
result: {
|
|
694
|
-
noFindings: 'The mixed commit has no review findings.',
|
|
695
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
696
|
-
needsBossReply: needsBossReplyDescription,
|
|
697
|
-
},
|
|
698
|
-
}),
|
|
699
|
-
onDone: withNeedsBossReplyTransition('reviewBossCommitMixed', [
|
|
700
|
-
{
|
|
701
|
-
guard: noFindingsAfter('continueIr'),
|
|
702
|
-
target: '#continueIr',
|
|
703
|
-
actions: rememberCaptainOutput,
|
|
704
|
-
},
|
|
705
|
-
{
|
|
706
|
-
guard: noFindingsAfter('summarizeSpecs'),
|
|
707
|
-
target: '#summarizeSpecs',
|
|
708
|
-
actions: rememberCaptainOutput,
|
|
709
|
-
},
|
|
710
|
-
{
|
|
711
|
-
guard: noFindingsAfter('done'),
|
|
712
|
-
target: '#done',
|
|
713
|
-
actions: rememberCaptainOutput,
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
guard: guardIs('hasFindings'),
|
|
717
|
-
target: '#respondToReview',
|
|
718
|
-
actions: [
|
|
719
|
-
rememberCaptainOutput,
|
|
720
|
-
assign({ reviewSubject: () => 'commit' }),
|
|
721
|
-
],
|
|
722
|
-
},
|
|
723
|
-
]),
|
|
724
|
-
onError: captainError,
|
|
725
|
-
},
|
|
726
|
-
},
|
|
727
|
-
reviewIrTaskCommitSpecs: {
|
|
728
|
-
id: 'reviewIrTaskCommitSpecs',
|
|
729
|
-
description: stateDescriptions.reviewIrTaskCommitSpecs,
|
|
730
|
-
meta: playbookMeta('reviewIrTaskCommitSpecs'),
|
|
731
|
-
tags: ['playbook.busy'],
|
|
732
|
-
invoke: {
|
|
733
|
-
src: 'player',
|
|
734
|
-
input: ({ context }) => ({
|
|
735
|
-
stateId: 'reviewIrTaskCommitSpecs',
|
|
736
|
-
player: 'Reviewer',
|
|
737
|
-
sourceItem: 'CODE-8',
|
|
738
|
-
...bossReplyInputFields(context),
|
|
739
|
-
irNumber: context.irNumber,
|
|
740
|
-
taskDescription: context.taskDescription,
|
|
741
|
-
prompt: [
|
|
742
|
-
'Review the latest commit.',
|
|
743
|
-
'Refer to the commit message.',
|
|
744
|
-
'Verify any affected spec items are:',
|
|
745
|
-
'',
|
|
746
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
747
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
748
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
749
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
750
|
-
'',
|
|
751
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
752
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
753
|
-
'Verify @specs/map.md reflects the changes.',
|
|
754
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
755
|
-
'Do not edit files or commit; report findings only.',
|
|
756
|
-
].join('\n'),
|
|
757
|
-
result: {
|
|
758
|
-
noFindings: 'The IR-task spec-only commit has no review findings.',
|
|
759
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
760
|
-
needsBossReply: needsBossReplyDescription,
|
|
761
|
-
},
|
|
762
|
-
}),
|
|
763
|
-
onDone: withNeedsBossReplyTransition('reviewIrTaskCommitSpecs', [
|
|
764
|
-
{
|
|
765
|
-
guard: noFindingsAfter('continueIr'),
|
|
766
|
-
target: '#continueIr',
|
|
767
|
-
actions: rememberCaptainOutput,
|
|
768
|
-
},
|
|
769
|
-
{
|
|
770
|
-
guard: noFindingsAfter('summarizeSpecs'),
|
|
771
|
-
target: '#summarizeSpecs',
|
|
772
|
-
actions: rememberCaptainOutput,
|
|
773
|
-
},
|
|
774
|
-
{
|
|
775
|
-
guard: noFindingsAfter('done'),
|
|
776
|
-
target: '#done',
|
|
777
|
-
actions: rememberCaptainOutput,
|
|
778
|
-
},
|
|
779
|
-
{
|
|
780
|
-
guard: guardIs('hasFindings'),
|
|
781
|
-
target: '#respondToReview',
|
|
782
|
-
actions: [
|
|
783
|
-
rememberCaptainOutput,
|
|
784
|
-
assign({ reviewSubject: () => 'commit' }),
|
|
785
|
-
],
|
|
786
|
-
},
|
|
787
|
-
]),
|
|
788
|
-
onError: captainError,
|
|
789
|
-
},
|
|
790
|
-
},
|
|
791
|
-
reviewIrTaskCommitCode: {
|
|
792
|
-
id: 'reviewIrTaskCommitCode',
|
|
793
|
-
description: stateDescriptions.reviewIrTaskCommitCode,
|
|
794
|
-
meta: playbookMeta('reviewIrTaskCommitCode'),
|
|
795
|
-
tags: ['playbook.busy'],
|
|
796
|
-
invoke: {
|
|
797
|
-
src: 'player',
|
|
798
|
-
input: ({ context }) => ({
|
|
799
|
-
stateId: 'reviewIrTaskCommitCode',
|
|
800
|
-
player: 'Reviewer',
|
|
801
|
-
sourceItem: 'CODE-9',
|
|
802
|
-
...bossReplyInputFields(context),
|
|
803
|
-
irNumber: context.irNumber,
|
|
804
|
-
taskDescription: context.taskDescription,
|
|
805
|
-
prompt: [
|
|
806
|
-
'Review the latest commit.',
|
|
807
|
-
'Refer to the commit message.',
|
|
808
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
809
|
-
"Think thoroughly — don't just approve or reject.",
|
|
810
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
811
|
-
'Verify @specs/map.md reflects the changes.',
|
|
812
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
813
|
-
'Do not edit files or commit; report findings only.',
|
|
814
|
-
].join('\n'),
|
|
815
|
-
result: {
|
|
816
|
-
noFindings: 'The IR-task code-only commit has no review findings.',
|
|
817
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
818
|
-
needsBossReply: needsBossReplyDescription,
|
|
819
|
-
},
|
|
820
|
-
}),
|
|
821
|
-
onDone: withNeedsBossReplyTransition('reviewIrTaskCommitCode', [
|
|
822
|
-
{
|
|
823
|
-
guard: noFindingsAfter('continueIr'),
|
|
824
|
-
target: '#continueIr',
|
|
825
|
-
actions: rememberCaptainOutput,
|
|
826
|
-
},
|
|
827
|
-
{
|
|
828
|
-
guard: noFindingsAfter('summarizeSpecs'),
|
|
829
|
-
target: '#summarizeSpecs',
|
|
830
|
-
actions: rememberCaptainOutput,
|
|
831
|
-
},
|
|
832
|
-
{
|
|
833
|
-
guard: noFindingsAfter('done'),
|
|
834
|
-
target: '#done',
|
|
835
|
-
actions: rememberCaptainOutput,
|
|
836
|
-
},
|
|
837
|
-
{
|
|
838
|
-
guard: guardIs('hasFindings'),
|
|
839
|
-
target: '#respondToReview',
|
|
840
|
-
actions: [
|
|
841
|
-
rememberCaptainOutput,
|
|
842
|
-
assign({ reviewSubject: () => 'commit' }),
|
|
843
|
-
],
|
|
844
|
-
},
|
|
845
|
-
]),
|
|
846
|
-
onError: captainError,
|
|
847
|
-
},
|
|
848
|
-
},
|
|
849
|
-
reviewIrTaskCommitMixed: {
|
|
850
|
-
id: 'reviewIrTaskCommitMixed',
|
|
851
|
-
description: stateDescriptions.reviewIrTaskCommitMixed,
|
|
852
|
-
meta: playbookMeta('reviewIrTaskCommitMixed'),
|
|
853
|
-
tags: ['playbook.busy'],
|
|
854
|
-
invoke: {
|
|
855
|
-
src: 'player',
|
|
856
|
-
input: ({ context }) => ({
|
|
857
|
-
stateId: 'reviewIrTaskCommitMixed',
|
|
858
|
-
player: 'Reviewer',
|
|
859
|
-
sourceItem: 'CODE-10',
|
|
860
|
-
...bossReplyInputFields(context),
|
|
861
|
-
irNumber: context.irNumber,
|
|
862
|
-
taskDescription: context.taskDescription,
|
|
863
|
-
prompt: [
|
|
864
|
-
'Review the latest commit.',
|
|
865
|
-
'Refer to the commit message.',
|
|
866
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
867
|
-
"Think thoroughly — don't just approve or reject.",
|
|
868
|
-
'Verify any affected spec items are:',
|
|
869
|
-
'',
|
|
870
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
871
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
872
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
873
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
874
|
-
'',
|
|
875
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
876
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
877
|
-
'Verify @specs/map.md reflects the changes.',
|
|
878
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
879
|
-
'Do not edit files or commit; report findings only.',
|
|
880
|
-
].join('\n'),
|
|
881
|
-
result: {
|
|
882
|
-
noFindings: 'The IR-task mixed commit has no review findings.',
|
|
883
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
884
|
-
needsBossReply: needsBossReplyDescription,
|
|
885
|
-
},
|
|
886
|
-
}),
|
|
887
|
-
onDone: withNeedsBossReplyTransition('reviewIrTaskCommitMixed', [
|
|
888
|
-
{
|
|
889
|
-
guard: noFindingsAfter('continueIr'),
|
|
890
|
-
target: '#continueIr',
|
|
891
|
-
actions: rememberCaptainOutput,
|
|
892
|
-
},
|
|
893
|
-
{
|
|
894
|
-
guard: noFindingsAfter('summarizeSpecs'),
|
|
895
|
-
target: '#summarizeSpecs',
|
|
896
|
-
actions: rememberCaptainOutput,
|
|
897
|
-
},
|
|
898
|
-
{
|
|
899
|
-
guard: noFindingsAfter('done'),
|
|
900
|
-
target: '#done',
|
|
901
|
-
actions: rememberCaptainOutput,
|
|
902
|
-
},
|
|
903
|
-
{
|
|
904
|
-
guard: guardIs('hasFindings'),
|
|
905
|
-
target: '#respondToReview',
|
|
906
|
-
actions: [
|
|
907
|
-
rememberCaptainOutput,
|
|
908
|
-
assign({ reviewSubject: () => 'commit' }),
|
|
909
|
-
],
|
|
910
|
-
},
|
|
911
|
-
]),
|
|
912
|
-
onError: captainError,
|
|
913
|
-
},
|
|
914
|
-
},
|
|
915
|
-
reviewChangesSpecs: {
|
|
916
|
-
id: 'reviewChangesSpecs',
|
|
917
|
-
description: stateDescriptions.reviewChangesSpecs,
|
|
918
|
-
meta: playbookMeta('reviewChangesSpecs'),
|
|
919
|
-
tags: ['playbook.busy'],
|
|
920
|
-
invoke: {
|
|
921
|
-
src: 'player',
|
|
922
|
-
input: ({ context }) => ({
|
|
923
|
-
stateId: 'reviewChangesSpecs',
|
|
924
|
-
player: 'Reviewer',
|
|
925
|
-
sourceItem: 'CODE-11',
|
|
926
|
-
...bossReplyInputFields(context),
|
|
927
|
-
prompt: [
|
|
928
|
-
'Review the unstaged and untracked changes in the context of the staged changes.',
|
|
929
|
-
'Understand the intent.',
|
|
930
|
-
'Verify any affected spec items are:',
|
|
931
|
-
'',
|
|
932
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
933
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
934
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
935
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
936
|
-
'',
|
|
937
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
938
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
939
|
-
'Verify @specs/map.md reflects the changes.',
|
|
940
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
941
|
-
'Do not edit files or commit; report findings only.',
|
|
942
|
-
].join('\n'),
|
|
943
|
-
result: {
|
|
944
|
-
noFindings: 'The uncommitted spec-only changes are ready to commit.',
|
|
945
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
946
|
-
needsBossReply: needsBossReplyDescription,
|
|
947
|
-
},
|
|
948
|
-
}),
|
|
949
|
-
onDone: withNeedsBossReplyTransition('reviewChangesSpecs', [
|
|
950
|
-
{
|
|
951
|
-
guard: guardIs('noFindings'),
|
|
952
|
-
target: '#commitJoint',
|
|
953
|
-
actions: rememberCaptainOutput,
|
|
954
|
-
},
|
|
955
|
-
{
|
|
956
|
-
guard: guardIs('hasFindings'),
|
|
957
|
-
target: '#respondToReview',
|
|
958
|
-
actions: [
|
|
959
|
-
rememberCaptainOutput,
|
|
960
|
-
assign({ reviewSubject: () => 'changes' }),
|
|
961
|
-
],
|
|
962
|
-
},
|
|
963
|
-
]),
|
|
964
|
-
onError: captainError,
|
|
965
|
-
},
|
|
966
|
-
},
|
|
967
|
-
reviewChangesCode: {
|
|
968
|
-
id: 'reviewChangesCode',
|
|
969
|
-
description: stateDescriptions.reviewChangesCode,
|
|
970
|
-
meta: playbookMeta('reviewChangesCode'),
|
|
971
|
-
tags: ['playbook.busy'],
|
|
972
|
-
invoke: {
|
|
973
|
-
src: 'player',
|
|
974
|
-
input: ({ context }) => ({
|
|
975
|
-
stateId: 'reviewChangesCode',
|
|
976
|
-
player: 'Reviewer',
|
|
977
|
-
sourceItem: 'CODE-12',
|
|
978
|
-
...bossReplyInputFields(context),
|
|
979
|
-
prompt: [
|
|
980
|
-
'Review the unstaged and untracked changes in the context of the staged changes.',
|
|
981
|
-
'Understand the intent.',
|
|
982
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
983
|
-
"Think thoroughly — don't just approve or reject.",
|
|
984
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
985
|
-
'Verify @specs/map.md reflects the changes.',
|
|
986
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
987
|
-
'Do not edit files or commit; report findings only.',
|
|
988
|
-
].join('\n'),
|
|
989
|
-
result: {
|
|
990
|
-
noFindings: 'The uncommitted code-only changes are ready to commit.',
|
|
991
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
992
|
-
needsBossReply: needsBossReplyDescription,
|
|
993
|
-
},
|
|
994
|
-
}),
|
|
995
|
-
onDone: withNeedsBossReplyTransition('reviewChangesCode', [
|
|
996
|
-
{
|
|
997
|
-
guard: guardIs('noFindings'),
|
|
998
|
-
target: '#commitJoint',
|
|
999
|
-
actions: rememberCaptainOutput,
|
|
1000
|
-
},
|
|
1001
|
-
{
|
|
1002
|
-
guard: guardIs('hasFindings'),
|
|
1003
|
-
target: '#respondToReview',
|
|
1004
|
-
actions: [
|
|
1005
|
-
rememberCaptainOutput,
|
|
1006
|
-
assign({ reviewSubject: () => 'changes' }),
|
|
1007
|
-
],
|
|
1008
|
-
},
|
|
1009
|
-
]),
|
|
1010
|
-
onError: captainError,
|
|
1011
|
-
},
|
|
1012
|
-
},
|
|
1013
|
-
reviewChangesMixed: {
|
|
1014
|
-
id: 'reviewChangesMixed',
|
|
1015
|
-
description: stateDescriptions.reviewChangesMixed,
|
|
1016
|
-
meta: playbookMeta('reviewChangesMixed'),
|
|
1017
|
-
tags: ['playbook.busy'],
|
|
1018
|
-
invoke: {
|
|
1019
|
-
src: 'player',
|
|
1020
|
-
input: ({ context }) => ({
|
|
1021
|
-
stateId: 'reviewChangesMixed',
|
|
1022
|
-
player: 'Reviewer',
|
|
1023
|
-
sourceItem: 'CODE-13',
|
|
1024
|
-
...bossReplyInputFields(context),
|
|
1025
|
-
prompt: [
|
|
1026
|
-
'Review the unstaged and untracked changes in the context of the staged changes.',
|
|
1027
|
-
'Understand the intent.',
|
|
1028
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
1029
|
-
"Think thoroughly — don't just approve or reject.",
|
|
1030
|
-
'Verify any affected spec items are:',
|
|
1031
|
-
'',
|
|
1032
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
1033
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
1034
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
1035
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
1036
|
-
'',
|
|
1037
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
1038
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
1039
|
-
'Verify @specs/map.md reflects the changes.',
|
|
1040
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
1041
|
-
'Do not edit files or commit; report findings only.',
|
|
1042
|
-
].join('\n'),
|
|
1043
|
-
result: {
|
|
1044
|
-
noFindings: 'The uncommitted mixed changes are ready to commit.',
|
|
1045
|
-
hasFindings: 'The review produced findings for Coder. Output shall include `reviews: <numbered list of findings, no duplication>`.',
|
|
1046
|
-
needsBossReply: needsBossReplyDescription,
|
|
1047
|
-
},
|
|
475
|
+
sourceItem: 'CODE-1',
|
|
476
|
+
prompt: FIRST_PHASE_PROMPT,
|
|
477
|
+
result: FIRST_PHASE_RESULTS,
|
|
478
|
+
callerInput: context.callerInput ?? '',
|
|
479
|
+
runResults: context.runResults,
|
|
480
|
+
coderPlayer: context.coderPlayer,
|
|
481
|
+
...(context.pendingBossQuestion === undefined
|
|
482
|
+
? {}
|
|
483
|
+
: { pendingBossQuestion: context.pendingBossQuestion }),
|
|
484
|
+
...(context.bossReply === undefined
|
|
485
|
+
? {}
|
|
486
|
+
: { bossReply: context.bossReply }),
|
|
1048
487
|
}),
|
|
1049
|
-
onDone:
|
|
488
|
+
onDone: [
|
|
1050
489
|
{
|
|
1051
|
-
guard:
|
|
1052
|
-
target: '
|
|
1053
|
-
actions:
|
|
490
|
+
guard: 'isDirectCommit',
|
|
491
|
+
target: 'reviewFirstCommit',
|
|
492
|
+
actions: 'rememberDirectCommit',
|
|
1054
493
|
},
|
|
1055
494
|
{
|
|
1056
|
-
guard:
|
|
1057
|
-
target: '
|
|
1058
|
-
actions:
|
|
1059
|
-
rememberCaptainOutput,
|
|
1060
|
-
assign({ reviewSubject: () => 'changes' }),
|
|
1061
|
-
],
|
|
1062
|
-
},
|
|
1063
|
-
]),
|
|
1064
|
-
onError: captainError,
|
|
1065
|
-
},
|
|
1066
|
-
},
|
|
1067
|
-
reviewChangesAndChallengesSpecs: {
|
|
1068
|
-
id: 'reviewChangesAndChallengesSpecs',
|
|
1069
|
-
description: stateDescriptions.reviewChangesAndChallengesSpecs,
|
|
1070
|
-
meta: playbookMeta('reviewChangesAndChallengesSpecs'),
|
|
1071
|
-
tags: ['playbook.busy'],
|
|
1072
|
-
invoke: {
|
|
1073
|
-
src: 'player',
|
|
1074
|
-
input: ({ context }) => ({
|
|
1075
|
-
stateId: 'reviewChangesAndChallengesSpecs',
|
|
1076
|
-
player: 'Reviewer',
|
|
1077
|
-
sourceItem: 'CODE-15',
|
|
1078
|
-
...bossReplyInputFields(context),
|
|
1079
|
-
reviews: context.reviews,
|
|
1080
|
-
challenges: context.challenges,
|
|
1081
|
-
prompt: [
|
|
1082
|
-
'Review the unstaged and untracked changes in the context of the staged changes.',
|
|
1083
|
-
'Understand the intent.',
|
|
1084
|
-
'Verify any affected spec items are:',
|
|
1085
|
-
'',
|
|
1086
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
1087
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
1088
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
1089
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
1090
|
-
'',
|
|
1091
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
1092
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
1093
|
-
'Verify @specs/map.md reflects the changes.',
|
|
1094
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
1095
|
-
'Do not edit files or commit; report findings only.',
|
|
1096
|
-
'For each rebuttal below, challenge or accept it, with strong reasoning, solid evidence, and comprehensive thinking.',
|
|
1097
|
-
].join('\n'),
|
|
1098
|
-
result: {
|
|
1099
|
-
approved: 'The new spec-only changes are ready to commit and every rebuttal was accepted (or no items remained open).',
|
|
1100
|
-
needsRevision: 'The combined round needs more work: the review produced findings, one or more rebuttals were rejected, or both. Output shall include `reviews: <numbered list of any new findings or rejected rebuttals to address>`.',
|
|
1101
|
-
needsBossReply: needsBossReplyDescription,
|
|
495
|
+
guard: 'isIrCommit',
|
|
496
|
+
target: 'reviewFirstCommit',
|
|
497
|
+
actions: 'rememberIrCommit',
|
|
1102
498
|
},
|
|
1103
|
-
}),
|
|
1104
|
-
onDone: withNeedsBossReplyTransition('reviewChangesAndChallengesSpecs', [
|
|
1105
499
|
{
|
|
1106
|
-
guard:
|
|
1107
|
-
target: '
|
|
1108
|
-
actions:
|
|
500
|
+
guard: 'needsBossReply',
|
|
501
|
+
target: 'awaitBossReply',
|
|
502
|
+
actions: 'rememberPendingQuestion',
|
|
1109
503
|
},
|
|
1110
504
|
{
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
actions: [
|
|
1114
|
-
rememberCaptainOutput,
|
|
1115
|
-
assign({ reviewSubject: () => 'changes' }),
|
|
1116
|
-
],
|
|
505
|
+
target: 'failed',
|
|
506
|
+
actions: 'rememberMalformedPlayerOutput',
|
|
1117
507
|
},
|
|
1118
|
-
]
|
|
1119
|
-
onError:
|
|
508
|
+
],
|
|
509
|
+
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
1120
510
|
},
|
|
1121
511
|
},
|
|
1122
|
-
|
|
1123
|
-
id: '
|
|
1124
|
-
description:
|
|
1125
|
-
meta: playbookMeta('
|
|
1126
|
-
tags: ['playbook.
|
|
512
|
+
reviewFirstCommit: {
|
|
513
|
+
id: 'reviewFirstCommit',
|
|
514
|
+
description: STATE_DESCRIPTIONS.reviewFirstCommit,
|
|
515
|
+
meta: playbookMeta('reviewFirstCommit'),
|
|
516
|
+
tags: ['playbook.suspended'],
|
|
1127
517
|
invoke: {
|
|
1128
|
-
src: '
|
|
518
|
+
src: 'playbook',
|
|
1129
519
|
input: ({ context }) => ({
|
|
1130
|
-
stateId: '
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
reviews: context.reviews,
|
|
1135
|
-
challenges: context.challenges,
|
|
1136
|
-
prompt: [
|
|
1137
|
-
'Review the unstaged and untracked changes in the context of the staged changes.',
|
|
1138
|
-
'Understand the intent.',
|
|
1139
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
1140
|
-
"Think thoroughly — don't just approve or reject.",
|
|
1141
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
1142
|
-
'Verify @specs/map.md reflects the changes.',
|
|
1143
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
1144
|
-
'Do not edit files or commit; report findings only.',
|
|
1145
|
-
'For each rebuttal below, challenge or accept it, with strong reasoning, solid evidence, and comprehensive thinking.',
|
|
1146
|
-
].join('\n'),
|
|
1147
|
-
result: {
|
|
1148
|
-
approved: 'The new code-only changes are ready to commit and every rebuttal was accepted (or no items remained open).',
|
|
1149
|
-
needsRevision: 'The combined round needs more work: the review produced findings, one or more rebuttals were rejected, or both. Output shall include `reviews: <numbered list of any new findings or rejected rebuttals to address>`.',
|
|
1150
|
-
needsBossReply: needsBossReplyDescription,
|
|
1151
|
-
},
|
|
520
|
+
stateId: 'reviewFirstCommit',
|
|
521
|
+
sourceItem: 'CODE-2',
|
|
522
|
+
playbookId: 'review',
|
|
523
|
+
text: initialReviewText(context),
|
|
1152
524
|
}),
|
|
1153
|
-
onDone:
|
|
525
|
+
onDone: [
|
|
1154
526
|
{
|
|
1155
|
-
guard:
|
|
1156
|
-
target: '
|
|
1157
|
-
actions:
|
|
527
|
+
guard: 'reviewApprovedDirect',
|
|
528
|
+
target: 'done',
|
|
529
|
+
actions: 'completeSuccessfully',
|
|
1158
530
|
},
|
|
1159
531
|
{
|
|
1160
|
-
guard:
|
|
1161
|
-
target: '
|
|
1162
|
-
actions: [
|
|
1163
|
-
rememberCaptainOutput,
|
|
1164
|
-
assign({ reviewSubject: () => 'changes' }),
|
|
1165
|
-
],
|
|
1166
|
-
},
|
|
1167
|
-
]),
|
|
1168
|
-
onError: captainError,
|
|
1169
|
-
},
|
|
1170
|
-
},
|
|
1171
|
-
reviewChangesAndChallengesMixed: {
|
|
1172
|
-
id: 'reviewChangesAndChallengesMixed',
|
|
1173
|
-
description: stateDescriptions.reviewChangesAndChallengesMixed,
|
|
1174
|
-
meta: playbookMeta('reviewChangesAndChallengesMixed'),
|
|
1175
|
-
tags: ['playbook.busy'],
|
|
1176
|
-
invoke: {
|
|
1177
|
-
src: 'player',
|
|
1178
|
-
input: ({ context }) => ({
|
|
1179
|
-
stateId: 'reviewChangesAndChallengesMixed',
|
|
1180
|
-
player: 'Reviewer',
|
|
1181
|
-
sourceItem: 'CODE-17',
|
|
1182
|
-
...bossReplyInputFields(context),
|
|
1183
|
-
reviews: context.reviews,
|
|
1184
|
-
challenges: context.challenges,
|
|
1185
|
-
prompt: [
|
|
1186
|
-
'Review the unstaged and untracked changes in the context of the staged changes.',
|
|
1187
|
-
'Understand the intent.',
|
|
1188
|
-
'Flag any issues or improvements (numbered; no duplication).',
|
|
1189
|
-
"Think thoroughly — don't just approve or reject.",
|
|
1190
|
-
'Verify any affected spec items are:',
|
|
1191
|
-
'',
|
|
1192
|
-
'- Complete & coherent: sufficient for you to reimplement code.',
|
|
1193
|
-
'- Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.',
|
|
1194
|
-
'- Minimal: essential and concise; every item earns its place; also check with other items.',
|
|
1195
|
-
'- Well organized: spec packages are finely scoped, with high cohesion and low coupling.',
|
|
1196
|
-
'',
|
|
1197
|
-
'Flag anything missing, redundant, over-specified, or under-specified.',
|
|
1198
|
-
'For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.',
|
|
1199
|
-
'Verify @specs/map.md reflects the changes.',
|
|
1200
|
-
"If the change is ready to commit or push, don't raise nitpicks.",
|
|
1201
|
-
'Do not edit files or commit; report findings only.',
|
|
1202
|
-
'For each rebuttal below, challenge or accept it, with strong reasoning, solid evidence, and comprehensive thinking.',
|
|
1203
|
-
].join('\n'),
|
|
1204
|
-
result: {
|
|
1205
|
-
approved: 'The new mixed changes are ready to commit and every rebuttal was accepted (or no items remained open).',
|
|
1206
|
-
needsRevision: 'The combined round needs more work: the review produced findings, one or more rebuttals were rejected, or both. Output shall include `reviews: <numbered list of any new findings or rejected rebuttals to address>`.',
|
|
1207
|
-
needsBossReply: needsBossReplyDescription,
|
|
532
|
+
guard: 'reviewApprovedIrCreated',
|
|
533
|
+
target: 'runIrTask',
|
|
1208
534
|
},
|
|
1209
|
-
}),
|
|
1210
|
-
onDone: withNeedsBossReplyTransition('reviewChangesAndChallengesMixed', [
|
|
1211
535
|
{
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
actions: rememberCaptainOutput,
|
|
536
|
+
target: 'done',
|
|
537
|
+
actions: 'completeWithInvalidReviewOutput',
|
|
1215
538
|
},
|
|
539
|
+
],
|
|
540
|
+
onError: [
|
|
1216
541
|
{
|
|
1217
|
-
guard:
|
|
1218
|
-
target: '
|
|
1219
|
-
actions:
|
|
1220
|
-
rememberCaptainOutput,
|
|
1221
|
-
assign({ reviewSubject: () => 'changes' }),
|
|
1222
|
-
],
|
|
542
|
+
guard: 'authoredReviewFailure',
|
|
543
|
+
target: 'done',
|
|
544
|
+
actions: 'completeWithReviewFailure',
|
|
1223
545
|
},
|
|
1224
|
-
|
|
1225
|
-
|
|
546
|
+
{ target: 'failed', actions: 'rememberActorError' },
|
|
547
|
+
],
|
|
1226
548
|
},
|
|
1227
549
|
},
|
|
1228
|
-
|
|
1229
|
-
id: '
|
|
1230
|
-
description:
|
|
1231
|
-
meta: playbookMeta('
|
|
550
|
+
runIrTask: {
|
|
551
|
+
id: 'runIrTask',
|
|
552
|
+
description: STATE_DESCRIPTIONS.runIrTask,
|
|
553
|
+
meta: playbookMeta('runIrTask', 'Coder'),
|
|
1232
554
|
tags: ['playbook.busy'],
|
|
1233
555
|
invoke: {
|
|
1234
556
|
src: 'player',
|
|
1235
557
|
input: ({ context }) => ({
|
|
1236
|
-
stateId: '
|
|
1237
|
-
player: '
|
|
1238
|
-
sourceItem: 'CODE-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
558
|
+
stateId: 'runIrTask',
|
|
559
|
+
player: 'Coder',
|
|
560
|
+
sourceItem: 'CODE-3',
|
|
561
|
+
prompt: IR_TASK_PROMPT,
|
|
562
|
+
result: IR_TASK_RESULTS,
|
|
563
|
+
callerInput: context.callerInput ?? '',
|
|
564
|
+
runResults: context.runResults,
|
|
565
|
+
coderPlayer: context.coderPlayer,
|
|
566
|
+
...(context.irNumber === undefined
|
|
567
|
+
? {}
|
|
568
|
+
: { irNumber: context.irNumber }),
|
|
569
|
+
...(context.irTask === undefined ? {} : { irTask: context.irTask }),
|
|
570
|
+
...(context.pendingBossQuestion === undefined
|
|
571
|
+
? {}
|
|
572
|
+
: { pendingBossQuestion: context.pendingBossQuestion }),
|
|
573
|
+
...(context.bossReply === undefined
|
|
574
|
+
? {}
|
|
575
|
+
: { bossReply: context.bossReply }),
|
|
1252
576
|
}),
|
|
1253
|
-
onDone:
|
|
577
|
+
onDone: [
|
|
1254
578
|
{
|
|
1255
|
-
guard:
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
target: '#commitJoint',
|
|
1259
|
-
actions: rememberCaptainOutput,
|
|
579
|
+
guard: 'isMoreTasks',
|
|
580
|
+
target: 'reviewIrTask',
|
|
581
|
+
actions: 'rememberMoreTasks',
|
|
1260
582
|
},
|
|
1261
583
|
{
|
|
1262
|
-
guard:
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
context.afterReview === 'continueIr',
|
|
1266
|
-
target: '#continueIr',
|
|
1267
|
-
actions: rememberCaptainOutput,
|
|
584
|
+
guard: 'isFinalTask',
|
|
585
|
+
target: 'reviewIrTask',
|
|
586
|
+
actions: 'rememberFinalTask',
|
|
1268
587
|
},
|
|
1269
588
|
{
|
|
1270
|
-
guard:
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
context.afterReview === 'summarizeSpecs',
|
|
1274
|
-
target: '#summarizeSpecs',
|
|
1275
|
-
actions: rememberCaptainOutput,
|
|
589
|
+
guard: 'needsBossReply',
|
|
590
|
+
target: 'awaitBossReply',
|
|
591
|
+
actions: 'rememberPendingQuestion',
|
|
1276
592
|
},
|
|
1277
593
|
{
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
context.reviewSubject === 'commit' &&
|
|
1281
|
-
context.afterReview === 'done',
|
|
1282
|
-
target: '#done',
|
|
1283
|
-
actions: rememberCaptainOutput,
|
|
594
|
+
target: 'failed',
|
|
595
|
+
actions: 'rememberMalformedPlayerOutput',
|
|
1284
596
|
},
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
target: '#respondToReview',
|
|
1288
|
-
actions: rememberCaptainOutput,
|
|
1289
|
-
},
|
|
1290
|
-
]),
|
|
1291
|
-
onError: captainError,
|
|
597
|
+
],
|
|
598
|
+
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
1292
599
|
},
|
|
1293
600
|
},
|
|
1294
|
-
|
|
1295
|
-
id: '
|
|
1296
|
-
description:
|
|
1297
|
-
meta: playbookMeta('
|
|
1298
|
-
tags: ['playbook.
|
|
601
|
+
reviewIrTask: {
|
|
602
|
+
id: 'reviewIrTask',
|
|
603
|
+
description: STATE_DESCRIPTIONS.reviewIrTask,
|
|
604
|
+
meta: playbookMeta('reviewIrTask'),
|
|
605
|
+
tags: ['playbook.suspended'],
|
|
1299
606
|
invoke: {
|
|
1300
|
-
src: '
|
|
607
|
+
src: 'playbook',
|
|
1301
608
|
input: ({ context }) => ({
|
|
1302
|
-
stateId: '
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
coderPlayer: context.coderPlayer,
|
|
1307
|
-
committerPlayer: context.committerPlayer,
|
|
1308
|
-
prompt: [
|
|
1309
|
-
'Make a commit of the changes that belong in the repo, following @specs/packages/git.md (reread if necessary).',
|
|
1310
|
-
"If that spec is absent, follow the legacy @specs/dev/git.md; if neither exists, follow the repository's existing commit conventions and do not search elsewhere.",
|
|
1311
|
-
'Write the commit message concisely.',
|
|
1312
|
-
'Coder is <coder-llm>.',
|
|
1313
|
-
'Format the `Co-authored-by` `<model>` token as the conventional human form of the substituted id (e.g., `claude-opus-4-7` → `Claude-Opus-4.7`, `gpt-5.5` → `GPT-5.5`).',
|
|
1314
|
-
].join('\n'),
|
|
1315
|
-
result: {
|
|
1316
|
-
committedSpecs: 'Committed changes that touch only spec item files (@specs/packages/, @specs/compositions/, or legacy @specs/{user,dev,test}/).',
|
|
1317
|
-
committedCode: 'Committed changes that touch only files that are not spec item files (any other files, including @specs/ decision, intent, or legacy iteration records, @specs/map.md, and @specs/meta.md).',
|
|
1318
|
-
committedMixed: 'Committed changes that span both spec item files and other files.',
|
|
1319
|
-
noRelevantChanges: 'There are no relevant changes to commit.',
|
|
1320
|
-
needsBossInput: 'Committing requires additional Boss input.',
|
|
1321
|
-
needsBossReply: needsBossReplyDescription,
|
|
1322
|
-
},
|
|
609
|
+
stateId: 'reviewIrTask',
|
|
610
|
+
sourceItem: 'CODE-4',
|
|
611
|
+
playbookId: 'review',
|
|
612
|
+
text: irTaskReviewText(context),
|
|
1323
613
|
}),
|
|
1324
|
-
onDone:
|
|
1325
|
-
{
|
|
1326
|
-
guard: guardAndOrigin('committedSpecs', 'bossIntent'),
|
|
1327
|
-
target: '#reviewBossCommitSpecs',
|
|
1328
|
-
actions: rememberCaptainOutput,
|
|
1329
|
-
},
|
|
1330
|
-
{
|
|
1331
|
-
guard: guardAndOrigin('committedCode', 'bossIntent'),
|
|
1332
|
-
target: '#reviewBossCommitCode',
|
|
1333
|
-
actions: rememberCaptainOutput,
|
|
1334
|
-
},
|
|
1335
|
-
{
|
|
1336
|
-
guard: guardAndOrigin('committedMixed', 'bossIntent'),
|
|
1337
|
-
target: '#reviewBossCommitMixed',
|
|
1338
|
-
actions: rememberCaptainOutput,
|
|
1339
|
-
},
|
|
1340
|
-
{
|
|
1341
|
-
guard: guardAndOrigin('committedSpecs', 'irTask'),
|
|
1342
|
-
target: '#reviewIrTaskCommitSpecs',
|
|
1343
|
-
actions: rememberCaptainOutput,
|
|
1344
|
-
},
|
|
614
|
+
onDone: [
|
|
1345
615
|
{
|
|
1346
|
-
guard:
|
|
1347
|
-
target: '
|
|
1348
|
-
actions:
|
|
616
|
+
guard: 'reviewApprovedMoreTasks',
|
|
617
|
+
target: 'runIrTask',
|
|
618
|
+
actions: 'advanceToNextIrTask',
|
|
1349
619
|
},
|
|
1350
620
|
{
|
|
1351
|
-
guard:
|
|
1352
|
-
target: '
|
|
1353
|
-
actions:
|
|
621
|
+
guard: 'reviewApprovedFinalTask',
|
|
622
|
+
target: 'done',
|
|
623
|
+
actions: 'completeSuccessfully',
|
|
1354
624
|
},
|
|
1355
625
|
{
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
actions: rememberCaptainOutput,
|
|
626
|
+
target: 'done',
|
|
627
|
+
actions: 'completeWithInvalidReviewOutput',
|
|
1359
628
|
},
|
|
629
|
+
],
|
|
630
|
+
onError: [
|
|
1360
631
|
{
|
|
1361
|
-
guard:
|
|
1362
|
-
target: '
|
|
1363
|
-
actions:
|
|
632
|
+
guard: 'authoredReviewFailure',
|
|
633
|
+
target: 'done',
|
|
634
|
+
actions: 'completeWithReviewFailure',
|
|
1364
635
|
},
|
|
1365
|
-
|
|
1366
|
-
|
|
636
|
+
{ target: 'failed', actions: 'rememberActorError' },
|
|
637
|
+
],
|
|
1367
638
|
},
|
|
1368
639
|
},
|
|
1369
|
-
|
|
1370
|
-
id: '
|
|
1371
|
-
description:
|
|
1372
|
-
meta: playbookMeta('
|
|
1373
|
-
tags: ['playbook.
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
input: ({ context }) => ({
|
|
1377
|
-
stateId: 'commitJoint',
|
|
1378
|
-
player: 'Committer',
|
|
1379
|
-
sourceItem: 'CODE-19',
|
|
1380
|
-
...bossReplyInputFields(context),
|
|
1381
|
-
coderPlayer: context.coderPlayer,
|
|
1382
|
-
reviewerPlayer: context.reviewerPlayer,
|
|
1383
|
-
committerPlayer: context.committerPlayer,
|
|
1384
|
-
prompt: [
|
|
1385
|
-
'Make a commit of the changes that belong in the repo, following @specs/packages/git.md (reread if necessary).',
|
|
1386
|
-
"If that spec is absent, follow the legacy @specs/dev/git.md; if neither exists, follow the repository's existing commit conventions and do not search elsewhere.",
|
|
1387
|
-
'Write the commit message concisely.',
|
|
1388
|
-
'Coder is <coder-llm>; Reviewer is <reviewer-llm>.',
|
|
1389
|
-
'Format the `Co-authored-by` `<model>` token as the conventional human form of the substituted id (e.g., `claude-opus-4-7` → `Claude-Opus-4.7`, `gpt-5.5` → `GPT-5.5`).',
|
|
1390
|
-
].join('\n'),
|
|
1391
|
-
result: {
|
|
1392
|
-
committed: 'Relevant changes were committed.',
|
|
1393
|
-
noRelevantChanges: 'There are no relevant changes to commit.',
|
|
1394
|
-
needsBossInput: 'Committing requires additional Boss input.',
|
|
1395
|
-
needsBossReply: needsBossReplyDescription,
|
|
1396
|
-
},
|
|
1397
|
-
}),
|
|
1398
|
-
onDone: withNeedsBossReplyTransition('commitJoint', [
|
|
1399
|
-
{
|
|
1400
|
-
guard: ({ context, event }) => outputOf(event)?.guard === 'committed' &&
|
|
1401
|
-
context.afterReview === 'continueIr',
|
|
1402
|
-
target: '#continueIr',
|
|
1403
|
-
actions: rememberCaptainOutput,
|
|
1404
|
-
},
|
|
1405
|
-
{
|
|
1406
|
-
guard: ({ context, event }) => outputOf(event)?.guard === 'committed' &&
|
|
1407
|
-
context.afterReview === 'summarizeSpecs',
|
|
1408
|
-
target: '#summarizeSpecs',
|
|
1409
|
-
actions: rememberCaptainOutput,
|
|
1410
|
-
},
|
|
640
|
+
awaitBossReply: {
|
|
641
|
+
id: 'awaitBossReply',
|
|
642
|
+
description: STATE_DESCRIPTIONS.awaitBossReply,
|
|
643
|
+
meta: playbookMeta('awaitBossReply'),
|
|
644
|
+
tags: ['playbook.parked'],
|
|
645
|
+
on: {
|
|
646
|
+
BOSS_REPLY: [
|
|
1411
647
|
{
|
|
1412
|
-
guard:
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
actions: rememberCaptainOutput,
|
|
648
|
+
guard: 'emptyBossReply',
|
|
649
|
+
target: '#failed',
|
|
650
|
+
actions: 'rememberEmptyBossReplyError',
|
|
1416
651
|
},
|
|
1417
652
|
{
|
|
1418
|
-
guard:
|
|
1419
|
-
target: '#
|
|
1420
|
-
|
|
653
|
+
guard: 'resumesFirstPhase',
|
|
654
|
+
target: '#runFirstPhase',
|
|
655
|
+
reenter: true,
|
|
656
|
+
actions: 'rememberBossReply',
|
|
1421
657
|
},
|
|
1422
658
|
{
|
|
1423
|
-
guard:
|
|
1424
|
-
target: '#
|
|
1425
|
-
|
|
659
|
+
guard: 'resumesIrTask',
|
|
660
|
+
target: '#runIrTask',
|
|
661
|
+
reenter: true,
|
|
662
|
+
actions: 'rememberBossReply',
|
|
1426
663
|
},
|
|
1427
|
-
]
|
|
1428
|
-
|
|
664
|
+
],
|
|
665
|
+
START_CODE: { target: 'runFirstPhase', actions: 'startCoding' },
|
|
1429
666
|
},
|
|
1430
667
|
},
|
|
1431
668
|
failed: {
|
|
1432
669
|
id: 'failed',
|
|
1433
|
-
description:
|
|
670
|
+
description: STATE_DESCRIPTIONS.failed,
|
|
1434
671
|
meta: playbookMeta('failed'),
|
|
1435
672
|
tags: ['playbook.parked'],
|
|
1436
|
-
on:
|
|
673
|
+
on: {
|
|
674
|
+
START_CODE: { target: 'runFirstPhase', actions: 'startCoding' },
|
|
675
|
+
},
|
|
1437
676
|
},
|
|
1438
677
|
done: {
|
|
1439
678
|
id: 'done',
|
|
1440
|
-
description:
|
|
679
|
+
description: STATE_DESCRIPTIONS.done,
|
|
1441
680
|
meta: playbookMeta('done'),
|
|
1442
681
|
type: 'final',
|
|
1443
682
|
},
|
|
1444
683
|
},
|
|
1445
684
|
});
|
|
685
|
+
export default codingMachine;
|