@sublang/playbook 15.0.0 → 16.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 +1 -1
- package/docs/embedding.md +9 -7
- package/package.json +3 -1
- package/reference/sdlc/branch.playbook/branch.fsm.d.ts +63 -33
- package/reference/sdlc/branch.playbook/branch.fsm.js +190 -108
- package/reference/sdlc/branch.playbook/branch.fsm.ts +269 -147
- package/reference/sdlc/branch.playbook/branch.gears.md +7 -17
- package/reference/sdlc/branch.playbook/branch.playbook.d.ts +12 -16
- package/reference/sdlc/branch.playbook/branch.playbook.js +112 -101
- package/reference/sdlc/branch.playbook/branch.playbook.ts +128 -171
- package/reference/sdlc/branch.playbook/branch.registry.d.ts +3 -2
- package/reference/sdlc/branch.playbook/branch.registry.ts +6 -2
- package/reference/sdlc/code.playbook/bin/repository-effects.js +54 -13
- package/reference/sdlc/code.playbook/code.fsm.d.ts +143 -90
- package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +8 -8
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +13 -13
- package/reference/sdlc/code.playbook/code.fsm.js +609 -649
- package/reference/sdlc/code.playbook/code.fsm.ts +890 -821
- package/reference/sdlc/code.playbook/code.gears.md +39 -29
- package/reference/sdlc/code.playbook/code.playbook.d.ts +13 -16
- package/reference/sdlc/code.playbook/code.playbook.js +166 -147
- package/reference/sdlc/code.playbook/code.playbook.ts +180 -229
- package/reference/sdlc/code.playbook/code.registry.d.ts +3 -2
- package/reference/sdlc/code.playbook/code.registry.ts +6 -2
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +42 -0
- package/reference/sdlc/code.playbook/playbook-captain.js +829 -194
- package/reference/sdlc/code.playbook/playbook-captain.ts +1227 -220
- package/reference/sdlc/decide.md +1 -0
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +270 -154
- package/reference/sdlc/decide.playbook/decide.fsm.js +765 -732
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1167 -945
- package/reference/sdlc/decide.playbook/decide.gears.md +28 -21
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +11 -67
- package/reference/sdlc/decide.playbook/decide.playbook.js +155 -3570
- package/reference/sdlc/decide.playbook/decide.playbook.ts +186 -5061
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +8 -4
- package/reference/sdlc/decide.playbook/decide.registry.js +2 -2
- package/reference/sdlc/decide.playbook/decide.registry.ts +13 -6
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +113 -131
- package/reference/sdlc/dev.playbook/dev.fsm.js +517 -575
- package/reference/sdlc/dev.playbook/dev.fsm.ts +667 -787
- package/reference/sdlc/dev.playbook/dev.gears.md +52 -54
- package/reference/sdlc/dev.playbook/dev.playbook.d.ts +13 -10
- package/reference/sdlc/dev.playbook/dev.playbook.js +128 -120
- package/reference/sdlc/dev.playbook/dev.playbook.ts +143 -193
- package/reference/sdlc/dev.playbook/dev.registry.d.ts +3 -2
- package/reference/sdlc/dev.playbook/dev.registry.ts +6 -2
- package/reference/sdlc/pr.playbook/pr.fsm.d.ts +92 -85
- package/reference/sdlc/pr.playbook/pr.fsm.js +461 -518
- package/reference/sdlc/pr.playbook/pr.fsm.ts +533 -663
- package/reference/sdlc/pr.playbook/pr.gears.md +47 -63
- package/reference/sdlc/pr.playbook/pr.playbook.d.ts +13 -23
- package/reference/sdlc/pr.playbook/pr.playbook.js +118 -112
- package/reference/sdlc/pr.playbook/pr.playbook.ts +136 -190
- package/reference/sdlc/pr.playbook/pr.registry.d.ts +3 -2
- package/reference/sdlc/pr.playbook/pr.registry.ts +4 -2
- package/reference/sdlc/review.md +3 -3
- package/reference/sdlc/review.playbook/review.fsm.d.ts +150 -116
- package/reference/sdlc/review.playbook/review.fsm.js +479 -550
- package/reference/sdlc/review.playbook/review.fsm.ts +717 -662
- package/reference/sdlc/review.playbook/review.gears.md +32 -29
- package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -12
- package/reference/sdlc/review.playbook/review.playbook.js +180 -155
- package/reference/sdlc/review.playbook/review.playbook.ts +190 -230
- package/reference/sdlc/review.playbook/review.registry.d.ts +6 -5
- package/reference/sdlc/review.playbook/review.registry.js +3 -3
- package/reference/sdlc/review.playbook/review.registry.ts +9 -5
- package/slc/gears2fsm.md +72 -3
- package/slc/link.md +50 -27
- package/slc/materialize-link.mjs +51 -13
- package/slc/optimize.md +4 -1
- package/slc/prefix-prompts.mjs +191 -0
- package/slc/prefix.md +203 -0
- package/slc/slc.pin-inputs.json +11 -1
- package/slc/text2gears.md +71 -11
- package/src/xstate-playbook-runtime.d.ts +82 -9
- package/src/xstate-playbook-runtime.js +1224 -283
- package/src/xstate-playbook-runtime.ts +1671 -338
- package/src/xstate-runtime.d.ts +3 -1
- package/src/xstate-runtime.js +9 -2
- package/src/xstate-runtime.ts +12 -2
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
//
|
|
4
|
-
//
|
|
4
|
+
// FSM object artifact compiled from ./dev.gears.md by gears2fsm.
|
|
5
|
+
// It defines only the machine, actor contracts, and typed inputs; the linked
|
|
6
|
+
// runtime supplies the `player` and `playbook` actor implementations.
|
|
5
7
|
|
|
6
8
|
import { assign, fromPromise, setup } from 'xstate';
|
|
9
|
+
import { validatePlaybookCallResult } from '@sublang/playbook/xstate-runtime';
|
|
10
|
+
import type { PlaybookCallResult } from '@sublang/playbook/runtime';
|
|
7
11
|
|
|
8
12
|
export type JsonValue =
|
|
9
13
|
| null
|
|
@@ -13,56 +17,59 @@ export type JsonValue =
|
|
|
13
17
|
| readonly JsonValue[]
|
|
14
18
|
| { readonly [key: string]: JsonValue };
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
/** DEV declares no parallel group. */
|
|
21
|
+
export const concurrentRoleSets: readonly (readonly string[])[] = [];
|
|
22
|
+
|
|
23
|
+
export type DevRole = 'analyst';
|
|
24
|
+
|
|
25
|
+
export type DevChildPlaybookId = 'code' | 'decide' | 'branch' | 'pr';
|
|
26
|
+
|
|
27
|
+
export type DevCallStateId =
|
|
19
28
|
| 'callCode'
|
|
20
29
|
| 'callDecide'
|
|
21
30
|
| 'callCodeAfterDecide'
|
|
31
|
+
| 'createBranch'
|
|
22
32
|
| 'openPullRequest';
|
|
23
33
|
|
|
24
|
-
export type
|
|
25
|
-
| 'DEV-1'
|
|
26
|
-
| 'DEV-2'
|
|
27
|
-
| 'DEV-3'
|
|
28
|
-
| 'DEV-4'
|
|
29
|
-
| 'DEV-5'
|
|
30
|
-
| 'DEV-6';
|
|
34
|
+
export type DevCallSourceItem = 'DEV-2' | 'DEV-3' | 'DEV-4' | 'DEV-5' | 'DEV-6';
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
type ResumableStateId = 'planAnalysis';
|
|
33
37
|
|
|
34
38
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
39
|
+
* The development path a pull-request planning outcome selected. It routes
|
|
40
|
+
* the `branch` success into the `code` call (DEV-2) or the `decide` call
|
|
41
|
+
* (DEV-3); its absence means a plain path that calls neither `branch` nor `pr`.
|
|
38
42
|
*/
|
|
39
43
|
export type DevPullRequestPath = 'code' | 'decide-then-code';
|
|
40
44
|
|
|
41
|
-
export
|
|
42
|
-
readonly questionId:
|
|
43
|
-
readonly resumeStateId:
|
|
45
|
+
export interface PendingBossQuestion {
|
|
46
|
+
readonly questionId: ResumableStateId;
|
|
47
|
+
readonly resumeStateId: ResumableStateId;
|
|
44
48
|
readonly sourceItem: 'DEV-1';
|
|
45
|
-
readonly asker: { readonly kind: 'role'; readonly roleId:
|
|
49
|
+
readonly asker: { readonly kind: 'role'; readonly roleId: DevRole };
|
|
46
50
|
readonly question: string;
|
|
47
|
-
}
|
|
51
|
+
}
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
type PendingBossQuestionParams = Omit<
|
|
54
|
+
PendingBossQuestion,
|
|
55
|
+
'questionId' | 'question'
|
|
56
|
+
>;
|
|
53
57
|
|
|
54
|
-
export
|
|
55
|
-
readonly stateId:
|
|
56
|
-
readonly role:
|
|
58
|
+
export interface PlayerInput {
|
|
59
|
+
readonly stateId: ResumableStateId;
|
|
60
|
+
readonly role: DevRole;
|
|
57
61
|
readonly sourceItem: 'DEV-1';
|
|
58
62
|
readonly prompt: string;
|
|
59
63
|
readonly result: Readonly<Record<string, string>>;
|
|
64
|
+
/** Substitutes `<development-request>`. */
|
|
60
65
|
readonly developmentRequest: string;
|
|
66
|
+
/** Substitutes `<discussion-context>`. */
|
|
61
67
|
readonly discussionContext: string;
|
|
68
|
+
/** Substitutes `<run-results>`. */
|
|
62
69
|
readonly runResults: string;
|
|
63
70
|
readonly pendingBossQuestion?: PendingBossQuestion;
|
|
64
71
|
readonly bossReply?: string;
|
|
65
|
-
}
|
|
72
|
+
}
|
|
66
73
|
|
|
67
74
|
export type PlayerOutput =
|
|
68
75
|
| { readonly guard: 'discussionComplete' }
|
|
@@ -75,60 +82,29 @@ export type PlayerOutput =
|
|
|
75
82
|
}
|
|
76
83
|
| { readonly guard: 'needsBossReply'; readonly question: string };
|
|
77
84
|
|
|
78
|
-
export
|
|
79
|
-
readonly stateId:
|
|
80
|
-
|
|
81
|
-
| 'callCode'
|
|
82
|
-
| 'callDecide'
|
|
83
|
-
| 'callCodeAfterDecide'
|
|
84
|
-
| 'openPullRequest';
|
|
85
|
-
readonly sourceItem: 'DEV-2' | 'DEV-3' | 'DEV-4' | 'DEV-5' | 'DEV-6';
|
|
85
|
+
export interface PlaybookInput {
|
|
86
|
+
readonly stateId: DevCallStateId;
|
|
87
|
+
readonly sourceItem: DevCallSourceItem;
|
|
86
88
|
readonly playbookId: DevChildPlaybookId;
|
|
87
89
|
readonly text: string;
|
|
88
|
-
}
|
|
90
|
+
}
|
|
89
91
|
|
|
90
|
-
/**
|
|
91
|
-
|
|
92
|
-
* structured terminal output before starting the dependent `code` call:
|
|
93
|
-
* the `decide`-owned commit, the exact evaluated repository revision, and
|
|
94
|
-
* the affirmative no-unsettled-findings fact. Additional members belong to
|
|
95
|
-
* DECIDE's own contract and do not disprove success.
|
|
96
|
-
*/
|
|
97
|
-
export type DecideSuccessOutput = {
|
|
98
|
-
readonly decideCommit: string;
|
|
99
|
-
readonly evaluatedRevision: string;
|
|
100
|
-
readonly noUnsettledFindings: true;
|
|
101
|
-
};
|
|
92
|
+
/** The child's JSON-safe machine output itself, delivered on `onDone`. */
|
|
93
|
+
export type PlaybookOutput = JsonValue | undefined;
|
|
102
94
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
* BRANCH's own contract and do not disprove success; a missing member is an
|
|
108
|
-
* insufficient result, never an empty default.
|
|
109
|
-
*/
|
|
110
|
-
export type BranchSuccessOutput = {
|
|
111
|
-
readonly branch: string;
|
|
112
|
-
readonly baseRevision: string;
|
|
113
|
-
readonly issueSummary: string;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* The fields DEV consumes from `code`'s canonical structured terminal output
|
|
118
|
-
* before starting the dependent `pr` call: the exact last `code`-owned commit
|
|
119
|
-
* and the exact final evaluated repository revision.
|
|
120
|
-
*/
|
|
121
|
-
export type CodeSuccessOutput = {
|
|
122
|
-
readonly lastCodeCommit: string;
|
|
123
|
-
readonly finalEvaluatedRevision: string;
|
|
124
|
-
};
|
|
95
|
+
export interface CompactError {
|
|
96
|
+
readonly name: string;
|
|
97
|
+
readonly message: string;
|
|
98
|
+
}
|
|
125
99
|
|
|
126
|
-
|
|
100
|
+
/** A control-plane error retained for inspection while DEV is parked. */
|
|
101
|
+
export interface ControlError {
|
|
127
102
|
readonly name: string;
|
|
128
103
|
readonly message: string;
|
|
129
|
-
|
|
104
|
+
readonly stack?: string;
|
|
105
|
+
}
|
|
130
106
|
|
|
131
|
-
/** Sanitized canonical child result
|
|
107
|
+
/** Sanitized canonical child result that DEV relays as its own outcome. */
|
|
132
108
|
export type CompletedChildResult =
|
|
133
109
|
| {
|
|
134
110
|
readonly playbookId: DevChildPlaybookId;
|
|
@@ -142,14 +118,12 @@ export type CompletedChildResult =
|
|
|
142
118
|
};
|
|
143
119
|
|
|
144
120
|
export type DevPlaybookOutput =
|
|
145
|
-
| {
|
|
146
|
-
readonly status: 'discussion-complete';
|
|
147
|
-
}
|
|
121
|
+
| { readonly status: 'discussion-complete' }
|
|
148
122
|
| {
|
|
149
123
|
readonly status: 'complete';
|
|
150
124
|
/** The final child of the selected path: `pr` on a pull-request path. */
|
|
151
125
|
readonly childPlaybookId: 'code' | 'pr';
|
|
152
|
-
/** The successful result of
|
|
126
|
+
/** The successful result of that final child call, when it has one. */
|
|
153
127
|
readonly childOutput?: JsonValue;
|
|
154
128
|
}
|
|
155
129
|
| {
|
|
@@ -158,46 +132,50 @@ export type DevPlaybookOutput =
|
|
|
158
132
|
readonly childResult: CompletedChildResult;
|
|
159
133
|
};
|
|
160
134
|
|
|
161
|
-
|
|
135
|
+
type DevCompletion =
|
|
136
|
+
| { readonly kind: 'discussion-complete' }
|
|
137
|
+
| {
|
|
138
|
+
readonly kind: 'complete';
|
|
139
|
+
readonly childPlaybookId: 'code' | 'pr';
|
|
140
|
+
readonly childOutput?: JsonValue;
|
|
141
|
+
}
|
|
142
|
+
| { readonly kind: 'child-failed'; readonly childResult: CompletedChildResult };
|
|
143
|
+
|
|
144
|
+
export interface DevInput {
|
|
145
|
+
/** Optional seed for `<run-results>`; absent means no relevant run results. */
|
|
162
146
|
readonly runResults?: string;
|
|
163
|
-
}
|
|
147
|
+
}
|
|
164
148
|
|
|
165
|
-
export
|
|
149
|
+
export interface DevContext {
|
|
166
150
|
readonly runResults: string;
|
|
167
151
|
readonly developmentRequest?: string;
|
|
168
|
-
|
|
152
|
+
/** Consumed Analyst Q&A for `<discussion-context>`; empty before any reply. */
|
|
153
|
+
readonly discussionContext: string;
|
|
169
154
|
readonly planningResult?: string;
|
|
155
|
+
readonly pullRequestPath?: DevPullRequestPath;
|
|
170
156
|
readonly decideCommit?: string;
|
|
171
157
|
readonly evaluatedRevision?: string;
|
|
172
|
-
/** DR-050: whether the accepted planning outcome selected a pull-request path. */
|
|
173
|
-
readonly deliveryViaPullRequest: boolean;
|
|
174
|
-
readonly pullRequestPath?: DevPullRequestPath;
|
|
175
158
|
readonly branch?: string;
|
|
176
159
|
readonly baseRevision?: string;
|
|
177
160
|
readonly issueSummary?: string;
|
|
178
161
|
readonly lastCodeCommit?: string;
|
|
179
162
|
readonly finalEvaluatedRevision?: string;
|
|
180
|
-
readonly completion?:
|
|
181
|
-
readonly
|
|
182
|
-
readonly childFailure?: CompletedChildResult;
|
|
183
|
-
readonly lastError?: unknown;
|
|
163
|
+
readonly completion?: DevCompletion;
|
|
164
|
+
readonly lastError?: ControlError;
|
|
184
165
|
readonly pendingBossQuestion?: PendingBossQuestion;
|
|
185
166
|
readonly bossReply?: string;
|
|
186
|
-
}
|
|
167
|
+
}
|
|
187
168
|
|
|
188
169
|
export type DevEvent =
|
|
189
170
|
| { readonly type: 'START_DEV'; readonly developmentRequest: string }
|
|
190
171
|
| {
|
|
191
172
|
readonly type: 'BOSS_REPLY';
|
|
192
173
|
readonly answer: string;
|
|
193
|
-
readonly questionId?:
|
|
174
|
+
readonly questionId?: string;
|
|
194
175
|
};
|
|
195
176
|
|
|
177
|
+
// DEV-1: the item's blockquote, verbatim, with the outer GEARS marker removed.
|
|
196
178
|
const PLAN_ANALYSIS_PROMPT = [
|
|
197
|
-
'> Original request: <development-request>',
|
|
198
|
-
'> Prior discussion: <discussion-context>',
|
|
199
|
-
'> Run results: <run-results>',
|
|
200
|
-
'',
|
|
201
179
|
'Plan which playbooks run for this request; the playbooks do the work.',
|
|
202
180
|
'Read the request, then the specs and the repository only as far as choosing the path requires.',
|
|
203
181
|
'Do not change files or commit while planning or discussing the request.',
|
|
@@ -216,79 +194,112 @@ const PLAN_ANALYSIS_PROMPT = [
|
|
|
216
194
|
'A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.',
|
|
217
195
|
'Do not choose `decide then code` merely because the work is large, nor `code` merely because it is small.',
|
|
218
196
|
'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
|
|
197
|
+
'',
|
|
198
|
+
'> Original request: <development-request>',
|
|
199
|
+
'> Prior discussion: <discussion-context>',
|
|
200
|
+
'> Run results: <run-results>',
|
|
219
201
|
].join('\n');
|
|
220
202
|
|
|
221
|
-
const
|
|
203
|
+
const NEEDS_BOSS_REPLY_DESCRIPTION =
|
|
204
|
+
"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>`.";
|
|
205
|
+
|
|
206
|
+
// DEV-1 local result contract: the authored `Results:` bullets in declared
|
|
207
|
+
// order, plus the universal Boss-reply suspension.
|
|
208
|
+
const PLAN_ANALYSIS_RESULT = {
|
|
222
209
|
discussionComplete:
|
|
223
|
-
'Analyst concluded the discussion
|
|
224
|
-
code:
|
|
225
|
-
'Analyst chose `code`: the existing decisions and spec items settle how the work is done. Output shall include `planningResult: <verbatim final text>`.',
|
|
210
|
+
'After a Boss reply that settles that no repository work should follow, Analyst concluded the discussion; dev completes without a child call or repository change.',
|
|
211
|
+
code: 'Analyst chose the code path because the existing decisions and spec items settle how the work is done. Output shall include `planningResult: <verbatim final text>`.',
|
|
226
212
|
decideThenCode:
|
|
227
|
-
'Analyst chose
|
|
213
|
+
'Analyst chose the decide then code path because the work turns on a rule, concept, term, shape, or trade-off the specs leave open or contradict. Output shall include `planningResult: <verbatim final text>`.',
|
|
228
214
|
codeViaPullRequest:
|
|
229
|
-
'Analyst chose
|
|
215
|
+
'Analyst chose the code via pull request path because the existing decisions and spec items settle how the work is done and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
|
|
230
216
|
decideThenCodeViaPullRequest:
|
|
231
|
-
'Analyst chose
|
|
232
|
-
needsBossReply:
|
|
233
|
-
"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>`.",
|
|
217
|
+
'Analyst chose the decide then code via pull request path because the work turns on a rule, concept, term, shape, or trade-off the specs leave open or contradict and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
|
|
218
|
+
needsBossReply: NEEDS_BOSS_REPLY_DESCRIPTION,
|
|
234
219
|
} as const;
|
|
235
220
|
|
|
221
|
+
// DEV-2 and DEV-3 and DEV-5: identical composed blockquotes.
|
|
222
|
+
const PLANNED_CALL_TEMPLATE = [
|
|
223
|
+
'> Original request: <development-request>',
|
|
224
|
+
'> Prior discussion: <discussion-context>',
|
|
225
|
+
'> Planning result: <planning-result>',
|
|
226
|
+
] as const;
|
|
227
|
+
|
|
228
|
+
// DEV-4.
|
|
229
|
+
const CODE_AFTER_DECIDE_CALL_TEMPLATE = [
|
|
230
|
+
'> Original request: <development-request>',
|
|
231
|
+
'> Prior discussion: <discussion-context>',
|
|
232
|
+
'> Planning result: <planning-result>',
|
|
233
|
+
'> DECIDE commit: <decide-commit>',
|
|
234
|
+
'> Evaluated revision: <evaluated-revision>',
|
|
235
|
+
] as const;
|
|
236
|
+
|
|
237
|
+
// DEV-6.
|
|
238
|
+
const PULL_REQUEST_CALL_TEMPLATE = [
|
|
239
|
+
'> Original request: <development-request>',
|
|
240
|
+
'> Issue summary: <issue-summary>',
|
|
241
|
+
'> Branch: <branch>',
|
|
242
|
+
'> Base revision: <base-revision>',
|
|
243
|
+
'> CODE commit: <last-code-commit>',
|
|
244
|
+
'> Evaluated revision: <final-evaluated-revision>',
|
|
245
|
+
] as const;
|
|
246
|
+
|
|
236
247
|
const STATE_DESCRIPTIONS = {
|
|
237
248
|
ready: 'Waiting for a development request.',
|
|
238
|
-
planAnalysis: 'Analyst
|
|
249
|
+
planAnalysis: 'Analyst chooses the development path in a short planning note.',
|
|
250
|
+
awaitBossReply: "Waiting for Boss to answer the acting agent's question.",
|
|
239
251
|
createBranch:
|
|
240
|
-
'The BRANCH playbook
|
|
241
|
-
callCode: 'The CODE playbook
|
|
252
|
+
'The BRANCH playbook prepares the branch for the pull-request delivery path.',
|
|
253
|
+
callCode: 'The CODE playbook implements the planned development path.',
|
|
242
254
|
callDecide:
|
|
243
|
-
'The DECIDE playbook
|
|
255
|
+
'The DECIDE playbook settles the decision the planned development path requires.',
|
|
244
256
|
callCodeAfterDecide:
|
|
245
|
-
'The CODE playbook
|
|
257
|
+
'The CODE playbook implements the development path DECIDE settled.',
|
|
246
258
|
openPullRequest:
|
|
247
|
-
'The PR playbook
|
|
248
|
-
awaitBossReply: 'Waiting for Boss to answer Analyst.',
|
|
259
|
+
'The PR playbook delivers the implemented branch through a pull request.',
|
|
249
260
|
failed:
|
|
250
|
-
'
|
|
261
|
+
'DEV failed outside a child result contract and waits for a new development request.',
|
|
251
262
|
discussionComplete:
|
|
252
|
-
'
|
|
253
|
-
done: "The selected development path completed with
|
|
263
|
+
'After a Boss reply, the planning discussion concluded with no child call or repository change.',
|
|
264
|
+
done: "The selected development path completed with its final child playbook's successful result.",
|
|
254
265
|
reportedChildFailure:
|
|
255
|
-
"
|
|
266
|
+
"DEV ended by relaying a child playbook's authored abort, failure, or result that did not prove the required success.",
|
|
256
267
|
} as const;
|
|
257
268
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
) {
|
|
269
|
+
type DevStateKey = keyof typeof STATE_DESCRIPTIONS;
|
|
270
|
+
|
|
271
|
+
function playbookMeta<const Key extends DevStateKey>(stateId: Key) {
|
|
262
272
|
return {
|
|
263
|
-
playbook: {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
273
|
+
playbook: { stateId, description: STATE_DESCRIPTIONS[stateId] },
|
|
274
|
+
} as const;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function roleMeta<const Key extends DevStateKey>(stateId: Key, role: DevRole) {
|
|
278
|
+
return {
|
|
279
|
+
playbook: { stateId, description: STATE_DESCRIPTIONS[stateId], role },
|
|
280
|
+
} as const;
|
|
269
281
|
}
|
|
270
282
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// machine rather than from DEV's output fields.
|
|
274
|
-
function terminalMeta<StateId extends keyof typeof STATE_DESCRIPTIONS>(
|
|
275
|
-
stateId: StateId,
|
|
283
|
+
function terminalMeta<const Key extends DevStateKey>(
|
|
284
|
+
stateId: Key,
|
|
276
285
|
terminal: 'success' | 'failure',
|
|
277
286
|
) {
|
|
278
287
|
return {
|
|
279
|
-
playbook: {
|
|
280
|
-
|
|
281
|
-
description: STATE_DESCRIPTIONS[stateId],
|
|
282
|
-
terminal,
|
|
283
|
-
},
|
|
284
|
-
};
|
|
288
|
+
playbook: { stateId, description: STATE_DESCRIPTIONS[stateId], terminal },
|
|
289
|
+
} as const;
|
|
285
290
|
}
|
|
286
291
|
|
|
292
|
+
const PLAN_ANALYSIS_QUESTION: PendingBossQuestionParams = {
|
|
293
|
+
resumeStateId: 'planAnalysis',
|
|
294
|
+
sourceItem: 'DEV-1',
|
|
295
|
+
asker: { kind: 'role', roleId: 'analyst' },
|
|
296
|
+
};
|
|
297
|
+
|
|
287
298
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
288
299
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
289
300
|
return false;
|
|
290
301
|
}
|
|
291
|
-
const prototype = Object.getPrototypeOf(value);
|
|
302
|
+
const prototype: unknown = Object.getPrototypeOf(value);
|
|
292
303
|
return prototype === Object.prototype || prototype === null;
|
|
293
304
|
}
|
|
294
305
|
|
|
@@ -296,146 +307,147 @@ function isNonEmptyString(value: unknown): value is string {
|
|
|
296
307
|
return typeof value === 'string' && value.trim().length > 0;
|
|
297
308
|
}
|
|
298
309
|
|
|
299
|
-
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// Player output (DEV-1), narrowed structurally from an unknown done event.
|
|
312
|
+
|
|
313
|
+
function playerOutputOf(event: unknown): Record<string, unknown> | undefined {
|
|
300
314
|
if (!isRecord(event)) return undefined;
|
|
301
315
|
return isRecord(event.output) ? event.output : undefined;
|
|
302
316
|
}
|
|
303
317
|
|
|
304
|
-
function
|
|
305
|
-
return
|
|
318
|
+
function hasGuard(event: unknown, guard: PlayerOutput['guard']): boolean {
|
|
319
|
+
return playerOutputOf(event)?.guard === guard;
|
|
306
320
|
}
|
|
307
321
|
|
|
308
|
-
function
|
|
309
|
-
const value =
|
|
322
|
+
function outputText(event: unknown, field: string): string | undefined {
|
|
323
|
+
const value = playerOutputOf(event)?.[field];
|
|
310
324
|
return isNonEmptyString(value) ? value : undefined;
|
|
311
325
|
}
|
|
312
326
|
|
|
313
|
-
function
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function isDecideThenCode({ event }: { event: unknown }): boolean {
|
|
321
|
-
return (
|
|
322
|
-
outputGuard(event, 'decideThenCode') &&
|
|
323
|
-
outputString(event, 'planningResult') !== undefined
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// DR-050: the two pull-request outcomes select the same development paths
|
|
328
|
-
// as `code` and `decide then code`, entered through `branch` first.
|
|
329
|
-
function isCodeViaPullRequest({ event }: { event: unknown }): boolean {
|
|
330
|
-
return (
|
|
331
|
-
outputGuard(event, 'codeViaPullRequest') &&
|
|
332
|
-
outputString(event, 'planningResult') !== undefined
|
|
333
|
-
);
|
|
327
|
+
function plannedPath(
|
|
328
|
+
guard: 'code' | 'decideThenCode' | 'codeViaPullRequest' | 'decideThenCodeViaPullRequest',
|
|
329
|
+
): (args: { event: unknown }) => boolean {
|
|
330
|
+
return ({ event }) =>
|
|
331
|
+
hasGuard(event, guard) && outputText(event, 'planningResult') !== undefined;
|
|
334
332
|
}
|
|
335
333
|
|
|
336
|
-
|
|
334
|
+
// Discussion complete is available only after a Boss reply: the Analyst
|
|
335
|
+
// invocation that returns it must have been resumed with Boss's answer.
|
|
336
|
+
function discussionCompleteAfterBossReply({
|
|
337
|
+
context,
|
|
337
338
|
event,
|
|
338
339
|
}: {
|
|
340
|
+
context: DevContext;
|
|
339
341
|
event: unknown;
|
|
340
342
|
}): boolean {
|
|
341
343
|
return (
|
|
342
|
-
|
|
343
|
-
|
|
344
|
+
hasGuard(event, 'discussionComplete') &&
|
|
345
|
+
context.pendingBossQuestion !== undefined &&
|
|
346
|
+
isNonEmptyString(context.bossReply)
|
|
344
347
|
);
|
|
345
348
|
}
|
|
346
349
|
|
|
347
|
-
|
|
348
|
-
// produced it must itself have been resumed with Boss's answer.
|
|
349
|
-
function isDiscussionComplete({
|
|
350
|
-
context,
|
|
351
|
-
event,
|
|
352
|
-
}: {
|
|
353
|
-
context: DevContext;
|
|
354
|
-
event: unknown;
|
|
355
|
-
}): boolean {
|
|
350
|
+
function needsBossReplyWithQuestion({ event }: { event: unknown }): boolean {
|
|
356
351
|
return (
|
|
357
|
-
|
|
358
|
-
isNonEmptyString(context.bossReply)
|
|
352
|
+
hasGuard(event, 'needsBossReply') && outputText(event, 'question') !== undefined
|
|
359
353
|
);
|
|
360
354
|
}
|
|
361
355
|
|
|
362
|
-
function
|
|
356
|
+
function needsBossReplyWithoutQuestion({ event }: { event: unknown }): boolean {
|
|
363
357
|
return (
|
|
364
|
-
|
|
365
|
-
outputString(event, 'question') !== undefined
|
|
358
|
+
hasGuard(event, 'needsBossReply') && outputText(event, 'question') === undefined
|
|
366
359
|
);
|
|
367
360
|
}
|
|
368
361
|
|
|
369
|
-
|
|
370
|
-
|
|
362
|
+
// ---------------------------------------------------------------------------
|
|
363
|
+
// Child outputs, read only from each child's canonical structured result and
|
|
364
|
+
// checked against the packaged public output interfaces.
|
|
365
|
+
|
|
366
|
+
function childOutputOf(event: unknown): PlaybookOutput {
|
|
367
|
+
return isRecord(event) ? (event.output as PlaybookOutput) : undefined;
|
|
371
368
|
}
|
|
372
369
|
|
|
373
|
-
function
|
|
374
|
-
return (
|
|
375
|
-
isRecord(value) &&
|
|
376
|
-
isNonEmptyString(value.decideCommit) &&
|
|
377
|
-
isNonEmptyString(value.evaluatedRevision) &&
|
|
378
|
-
value.noUnsettledFindings === true
|
|
379
|
-
);
|
|
370
|
+
function childErrorOf(event: unknown): unknown {
|
|
371
|
+
return isRecord(event) ? event.error : undefined;
|
|
380
372
|
}
|
|
381
373
|
|
|
382
|
-
|
|
383
|
-
|
|
374
|
+
interface BranchSuccess {
|
|
375
|
+
readonly branch: string;
|
|
376
|
+
readonly baseRevision: string;
|
|
377
|
+
readonly issueSummary: string;
|
|
384
378
|
}
|
|
385
379
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
380
|
+
function branchSuccessOf(value: unknown): BranchSuccess | undefined {
|
|
381
|
+
if (
|
|
382
|
+
!isRecord(value) ||
|
|
383
|
+
value.status !== 'branched' ||
|
|
384
|
+
!isNonEmptyString(value.branch) ||
|
|
385
|
+
!isNonEmptyString(value.baseRevision) ||
|
|
386
|
+
!isNonEmptyString(value.issueSummary)
|
|
387
|
+
) {
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
branch: value.branch,
|
|
392
|
+
baseRevision: value.baseRevision,
|
|
393
|
+
issueSummary: value.issueSummary,
|
|
394
|
+
};
|
|
397
395
|
}
|
|
398
396
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}: {
|
|
403
|
-
context: DevContext;
|
|
404
|
-
event: unknown;
|
|
405
|
-
}): boolean {
|
|
406
|
-
return (
|
|
407
|
-
context.pullRequestPath === 'code' &&
|
|
408
|
-
isBranchSuccessValue(childOutputOf(event))
|
|
409
|
-
);
|
|
397
|
+
interface DecideSuccess {
|
|
398
|
+
readonly decideCommit: string;
|
|
399
|
+
readonly evaluatedRevision: string;
|
|
410
400
|
}
|
|
411
401
|
|
|
412
|
-
function
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
402
|
+
function decideSuccessOf(value: unknown): DecideSuccess | undefined {
|
|
403
|
+
if (
|
|
404
|
+
!isRecord(value) ||
|
|
405
|
+
value.noUnsettledFindings !== true ||
|
|
406
|
+
!isNonEmptyString(value.decideCommit) ||
|
|
407
|
+
!isNonEmptyString(value.evaluatedRevision)
|
|
408
|
+
) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
decideCommit: value.decideCommit,
|
|
413
|
+
evaluatedRevision: value.evaluatedRevision,
|
|
414
|
+
};
|
|
423
415
|
}
|
|
424
416
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
value.
|
|
434
|
-
|
|
417
|
+
interface CodeSuccess {
|
|
418
|
+
readonly lastCodeCommit: string;
|
|
419
|
+
readonly finalEvaluatedRevision: string;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function codeSuccessOf(value: unknown): CodeSuccess | undefined {
|
|
423
|
+
if (
|
|
424
|
+
!isRecord(value) ||
|
|
425
|
+
value.status !== 'complete' ||
|
|
426
|
+
value.allReviewsPassed !== true ||
|
|
427
|
+
!isNonEmptyString(value.lastCodeCommit) ||
|
|
428
|
+
!isNonEmptyString(value.finalEvaluatedRevision)
|
|
429
|
+
) {
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
lastCodeCommit: value.lastCodeCommit,
|
|
434
|
+
finalEvaluatedRevision: value.finalEvaluatedRevision,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function branchSucceededFor(
|
|
439
|
+
path: DevPullRequestPath,
|
|
440
|
+
): (args: { context: DevContext; event: unknown }) => boolean {
|
|
441
|
+
return ({ context, event }) =>
|
|
442
|
+
context.pullRequestPath === path &&
|
|
443
|
+
branchSuccessOf(childOutputOf(event)) !== undefined;
|
|
435
444
|
}
|
|
436
445
|
|
|
437
|
-
|
|
438
|
-
|
|
446
|
+
function decideSucceeded({ event }: { event: unknown }): boolean {
|
|
447
|
+
return decideSuccessOf(childOutputOf(event)) !== undefined;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function codeSucceededOnPullRequestPath({
|
|
439
451
|
context,
|
|
440
452
|
event,
|
|
441
453
|
}: {
|
|
@@ -443,25 +455,12 @@ function isPlainCodeSuccess({
|
|
|
443
455
|
event: unknown;
|
|
444
456
|
}): boolean {
|
|
445
457
|
return (
|
|
446
|
-
context.
|
|
447
|
-
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// A pull-request path additionally consumes the exact final evaluated
|
|
452
|
-
// repository revision for the `pr` call, so a CODE result lacking it does not
|
|
453
|
-
// prove the success that path requires.
|
|
454
|
-
function isCodeSuccessValueForPullRequest(
|
|
455
|
-
value: unknown,
|
|
456
|
-
): value is CodeSuccessOutput & Record<string, unknown> {
|
|
457
|
-
return (
|
|
458
|
-
isCodeSuccessValue(value) &&
|
|
459
|
-
isRecord(value) &&
|
|
460
|
-
isNonEmptyString(value.finalEvaluatedRevision)
|
|
458
|
+
context.pullRequestPath !== undefined &&
|
|
459
|
+
codeSuccessOf(childOutputOf(event)) !== undefined
|
|
461
460
|
);
|
|
462
461
|
}
|
|
463
462
|
|
|
464
|
-
function
|
|
463
|
+
function codeSucceededOnPlainPath({
|
|
465
464
|
context,
|
|
466
465
|
event,
|
|
467
466
|
}: {
|
|
@@ -469,249 +468,62 @@ function isCodeSuccessViaPullRequest({
|
|
|
469
468
|
event: unknown;
|
|
470
469
|
}): boolean {
|
|
471
470
|
return (
|
|
472
|
-
context.
|
|
473
|
-
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function isStateValue(value: unknown, ancestors = new Set<object>()): boolean {
|
|
478
|
-
if (typeof value === 'string') return true;
|
|
479
|
-
if (!isRecord(value) || ancestors.has(value)) return false;
|
|
480
|
-
const next = new Set(ancestors).add(value);
|
|
481
|
-
return Reflect.ownKeys(value).every(
|
|
482
|
-
(key) =>
|
|
483
|
-
typeof key === 'string' &&
|
|
484
|
-
Object.prototype.propertyIsEnumerable.call(value, key) &&
|
|
485
|
-
isStateValue(value[key], next),
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function isStringArray(value: unknown): value is readonly string[] {
|
|
490
|
-
return (
|
|
491
|
-
Array.isArray(value) &&
|
|
492
|
-
Reflect.ownKeys(value).length === value.length + 1 &&
|
|
493
|
-
Reflect.ownKeys(value).every((key) => {
|
|
494
|
-
if (key === 'length') return true;
|
|
495
|
-
return (
|
|
496
|
-
typeof key === 'string' &&
|
|
497
|
-
/^(0|[1-9][0-9]*)$/.test(key) &&
|
|
498
|
-
Number(key) < value.length
|
|
499
|
-
);
|
|
500
|
-
}) &&
|
|
501
|
-
value.every((entry) => isNonEmptyString(entry)) &&
|
|
502
|
-
new Set(value).size === value.length
|
|
471
|
+
context.pullRequestPath === undefined &&
|
|
472
|
+
codeSuccessOf(childOutputOf(event)) !== undefined
|
|
503
473
|
);
|
|
504
474
|
}
|
|
505
475
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const allowed = new Set([
|
|
509
|
-
'value',
|
|
510
|
-
'activeStateIds',
|
|
511
|
-
'tags',
|
|
512
|
-
'status',
|
|
513
|
-
'quiescent',
|
|
514
|
-
'stateId',
|
|
515
|
-
]);
|
|
516
|
-
if (
|
|
517
|
-
Reflect.ownKeys(value).some(
|
|
518
|
-
(key) => typeof key !== 'string' || !allowed.has(key),
|
|
519
|
-
) ||
|
|
520
|
-
!Object.prototype.hasOwnProperty.call(value, 'value') ||
|
|
521
|
-
!Object.prototype.hasOwnProperty.call(value, 'activeStateIds') ||
|
|
522
|
-
!Object.prototype.hasOwnProperty.call(value, 'tags') ||
|
|
523
|
-
!Object.prototype.hasOwnProperty.call(value, 'status') ||
|
|
524
|
-
!Object.prototype.hasOwnProperty.call(value, 'quiescent')
|
|
525
|
-
) {
|
|
526
|
-
return false;
|
|
527
|
-
}
|
|
528
|
-
return (
|
|
529
|
-
isStateValue(value.value) &&
|
|
530
|
-
isStringArray(value.activeStateIds) &&
|
|
531
|
-
isStringArray(value.tags) &&
|
|
532
|
-
(value.status === 'active' ||
|
|
533
|
-
value.status === 'done' ||
|
|
534
|
-
value.status === 'error' ||
|
|
535
|
-
value.status === 'stopped') &&
|
|
536
|
-
typeof value.quiescent === 'boolean' &&
|
|
537
|
-
(!Object.prototype.hasOwnProperty.call(value, 'stateId') ||
|
|
538
|
-
(isNonEmptyString(value.stateId) &&
|
|
539
|
-
value.activeStateIds.length === 1 &&
|
|
540
|
-
value.activeStateIds[0] === value.stateId))
|
|
541
|
-
);
|
|
542
|
-
}
|
|
476
|
+
// ---------------------------------------------------------------------------
|
|
477
|
+
// Authored rejected child results.
|
|
543
478
|
|
|
544
|
-
function
|
|
479
|
+
export function authoredChildResult(
|
|
545
480
|
error: unknown,
|
|
546
|
-
|
|
481
|
+
expectedPlaybookId: string,
|
|
482
|
+
): PlaybookCallResult | undefined {
|
|
547
483
|
if (!(error instanceof Error)) return undefined;
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
// DR-048: a child that completed at an authored failure terminal. The
|
|
558
|
-
// bridge rejects it through the same error path, so DEV recognizes the
|
|
559
|
-
// failure from the child's own machine and never from its output fields.
|
|
560
|
-
| {
|
|
561
|
-
readonly status: 'ok';
|
|
562
|
-
readonly output?: JsonValue;
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
function isFailureTerminalRecord(value: unknown): boolean {
|
|
566
|
-
if (!isRecord(value)) return false;
|
|
567
|
-
const allowed = new Set(['stateId', 'kind', 'description']);
|
|
568
|
-
return (
|
|
569
|
-
Reflect.ownKeys(value).every(
|
|
570
|
-
(key) => typeof key === 'string' && allowed.has(key),
|
|
571
|
-
) &&
|
|
572
|
-
isNonEmptyString(value.stateId) &&
|
|
573
|
-
value.kind === 'failure' &&
|
|
574
|
-
(!Object.prototype.hasOwnProperty.call(value, 'description') ||
|
|
575
|
-
typeof value.description === 'string')
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function normalizedChildFailure(
|
|
580
|
-
error: unknown,
|
|
581
|
-
playbookId: DevChildPlaybookId,
|
|
582
|
-
): AuthoredChildFailure | undefined {
|
|
583
|
-
const result = nestedResultFromError(error);
|
|
584
|
-
if (result === undefined) return undefined;
|
|
585
|
-
const allowed = new Set([
|
|
586
|
-
'status',
|
|
587
|
-
'playbookId',
|
|
588
|
-
'childSessionId',
|
|
589
|
-
'state',
|
|
590
|
-
...(result.status === 'ok' ? ['output', 'terminal'] : ['error']),
|
|
591
|
-
]);
|
|
592
|
-
if (
|
|
593
|
-
Reflect.ownKeys(result).some(
|
|
594
|
-
(key) => typeof key !== 'string' || !allowed.has(key),
|
|
595
|
-
) ||
|
|
596
|
-
(result.status !== 'aborted' &&
|
|
597
|
-
result.status !== 'error' &&
|
|
598
|
-
result.status !== 'ok') ||
|
|
599
|
-
result.playbookId !== playbookId
|
|
600
|
-
) {
|
|
601
|
-
return undefined;
|
|
602
|
-
}
|
|
603
|
-
if (
|
|
604
|
-
Object.prototype.hasOwnProperty.call(result, 'childSessionId') &&
|
|
605
|
-
!isNonEmptyString(result.childSessionId)
|
|
606
|
-
) {
|
|
607
|
-
return undefined;
|
|
608
|
-
}
|
|
609
|
-
if (result.status === 'ok') {
|
|
610
|
-
if (
|
|
611
|
-
!Object.prototype.hasOwnProperty.call(result, 'terminal') ||
|
|
612
|
-
!isFailureTerminalRecord(result.terminal) ||
|
|
613
|
-
!isNonEmptyString(result.childSessionId) ||
|
|
614
|
-
(Object.prototype.hasOwnProperty.call(result, 'state') &&
|
|
615
|
-
!isPlaybookState(result.state))
|
|
616
|
-
) {
|
|
617
|
-
return undefined;
|
|
618
|
-
}
|
|
619
|
-
return {
|
|
620
|
-
status: 'ok',
|
|
621
|
-
...(result.output === undefined
|
|
622
|
-
? {}
|
|
623
|
-
: { output: result.output as JsonValue }),
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
if (
|
|
627
|
-
Object.prototype.hasOwnProperty.call(result, 'state') &&
|
|
628
|
-
!isPlaybookState(result.state)
|
|
629
|
-
) {
|
|
630
|
-
return undefined;
|
|
631
|
-
}
|
|
632
|
-
let normalizedError: CompactError | undefined;
|
|
633
|
-
if (Object.prototype.hasOwnProperty.call(result, 'error')) {
|
|
634
|
-
if (!isRecord(result.error)) return undefined;
|
|
635
|
-
const errorKeys = Reflect.ownKeys(result.error);
|
|
636
|
-
if (
|
|
637
|
-
errorKeys.some(
|
|
638
|
-
(key) =>
|
|
639
|
-
typeof key !== 'string' ||
|
|
640
|
-
(key !== 'name' && key !== 'message' && key !== 'stack'),
|
|
641
|
-
) ||
|
|
642
|
-
!Object.prototype.hasOwnProperty.call(result.error, 'name') ||
|
|
643
|
-
!Object.prototype.hasOwnProperty.call(result.error, 'message') ||
|
|
644
|
-
!isNonEmptyString(result.error.name) ||
|
|
645
|
-
typeof result.error.message !== 'string' ||
|
|
646
|
-
(Object.prototype.hasOwnProperty.call(result.error, 'stack') &&
|
|
647
|
-
typeof result.error.stack !== 'string')
|
|
648
|
-
) {
|
|
649
|
-
return undefined;
|
|
650
|
-
}
|
|
651
|
-
normalizedError = {
|
|
652
|
-
name: result.error.name,
|
|
653
|
-
message: result.error.message,
|
|
654
|
-
};
|
|
655
|
-
} else if (result.status === 'error') {
|
|
484
|
+
try {
|
|
485
|
+
const result = validatePlaybookCallResult(
|
|
486
|
+
(error as Error & { result?: unknown }).result,
|
|
487
|
+
expectedPlaybookId,
|
|
488
|
+
);
|
|
489
|
+
return result.status !== 'ok' || result.terminal?.kind === 'failure'
|
|
490
|
+
? result
|
|
491
|
+
: undefined;
|
|
492
|
+
} catch {
|
|
656
493
|
return undefined;
|
|
657
494
|
}
|
|
658
|
-
return {
|
|
659
|
-
status: result.status,
|
|
660
|
-
...(normalizedError === undefined ? {} : { error: normalizedError }),
|
|
661
|
-
};
|
|
662
495
|
}
|
|
663
496
|
|
|
664
|
-
function
|
|
497
|
+
function authoredResultFrom(
|
|
665
498
|
playbookId: DevChildPlaybookId,
|
|
666
499
|
): (args: { event: unknown }) => boolean {
|
|
667
|
-
return ({ event }) =>
|
|
668
|
-
|
|
669
|
-
return normalizedChildFailure(error, playbookId) !== undefined;
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function authoredChildError(
|
|
674
|
-
event: unknown,
|
|
675
|
-
playbookId: DevChildPlaybookId,
|
|
676
|
-
): CompactError {
|
|
677
|
-
const outer = isRecord(event) ? event.error : undefined;
|
|
678
|
-
const failure = normalizedChildFailure(outer, playbookId);
|
|
679
|
-
if (failure !== undefined && failure.status !== 'ok' && failure.error) {
|
|
680
|
-
return failure.error;
|
|
681
|
-
}
|
|
682
|
-
if (failure?.status === 'aborted') {
|
|
683
|
-
return {
|
|
684
|
-
name: 'AbortError',
|
|
685
|
-
message: `${playbookId.toUpperCase()} was aborted.`,
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
if (outer instanceof Error) {
|
|
689
|
-
return { name: outer.name || 'Error', message: outer.message };
|
|
690
|
-
}
|
|
691
|
-
return { name: 'Error', message: String(outer) };
|
|
500
|
+
return ({ event }) =>
|
|
501
|
+
authoredChildResult(childErrorOf(event), playbookId) !== undefined;
|
|
692
502
|
}
|
|
693
503
|
|
|
694
|
-
function
|
|
504
|
+
function relayedChildResult(
|
|
695
505
|
event: unknown,
|
|
696
506
|
playbookId: DevChildPlaybookId,
|
|
697
|
-
): CompletedChildResult {
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
if (failure?.status === 'ok') {
|
|
507
|
+
): CompletedChildResult | undefined {
|
|
508
|
+
const result = authoredChildResult(childErrorOf(event), playbookId);
|
|
509
|
+
if (result === undefined) return undefined;
|
|
510
|
+
if (result.status === 'ok') {
|
|
511
|
+
// A child that completed at its own authored failure terminal relays its
|
|
512
|
+
// own output; an absent output is omitted rather than stored.
|
|
704
513
|
return {
|
|
705
514
|
playbookId,
|
|
706
515
|
status: 'ok',
|
|
707
|
-
...(
|
|
516
|
+
...(result.output === undefined ? {} : { output: result.output }),
|
|
708
517
|
};
|
|
709
518
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
519
|
+
const error: CompactError =
|
|
520
|
+
result.error === undefined
|
|
521
|
+
? {
|
|
522
|
+
name: 'AbortError',
|
|
523
|
+
message: `The ${playbookId} playbook call was aborted.`,
|
|
524
|
+
}
|
|
525
|
+
: { name: result.error.name, message: result.error.message };
|
|
526
|
+
return { playbookId, status: result.status, error };
|
|
715
527
|
}
|
|
716
528
|
|
|
717
529
|
function insufficientChildResult(
|
|
@@ -722,106 +534,140 @@ function insufficientChildResult(
|
|
|
722
534
|
return {
|
|
723
535
|
playbookId,
|
|
724
536
|
status: 'ok',
|
|
725
|
-
...(output === undefined ? {} : { output
|
|
537
|
+
...(output === undefined ? {} : { output }),
|
|
726
538
|
};
|
|
727
539
|
}
|
|
728
540
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
541
|
+
function normalizedControlError(error: unknown): ControlError {
|
|
542
|
+
if (error instanceof Error) {
|
|
543
|
+
return {
|
|
544
|
+
name: error.name.length > 0 ? error.name : 'Error',
|
|
545
|
+
message: error.message,
|
|
546
|
+
...(typeof error.stack === 'string' ? { stack: error.stack } : {}),
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
name: 'Error',
|
|
551
|
+
message: typeof error === 'string' ? error : 'Actor failed with a non-Error value.',
|
|
552
|
+
};
|
|
739
553
|
}
|
|
740
554
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
555
|
+
// ---------------------------------------------------------------------------
|
|
556
|
+
// Discussion history and quoted-relay composition.
|
|
557
|
+
|
|
558
|
+
/** One consumed Analyst question and Boss reply, as relayed discussion. */
|
|
559
|
+
export function renderDiscussionExchange(question: string, answer: string): string {
|
|
560
|
+
return `Analyst question: ${question}\nBoss reply: ${answer}`;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Persists the answered pending Q&A pair into the typed discussion context
|
|
564
|
+
// before the pending fields are replaced or cleared, so later calls relay it.
|
|
565
|
+
function archivedDiscussion(context: DevContext): string {
|
|
744
566
|
if (
|
|
745
567
|
context.pendingBossQuestion === undefined ||
|
|
746
568
|
!isNonEmptyString(context.bossReply)
|
|
747
569
|
) {
|
|
748
|
-
return context.
|
|
570
|
+
return context.discussionContext;
|
|
749
571
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
|
|
572
|
+
const exchange = renderDiscussionExchange(
|
|
573
|
+
context.pendingBossQuestion.question,
|
|
574
|
+
context.bossReply,
|
|
575
|
+
);
|
|
576
|
+
return context.discussionContext.length > 0
|
|
577
|
+
? `${context.discussionContext}\n\n${exchange}`
|
|
578
|
+
: exchange;
|
|
757
579
|
}
|
|
758
580
|
|
|
759
|
-
|
|
760
|
-
return values
|
|
761
|
-
.filter(([, value]) => value.length > 0)
|
|
762
|
-
.map(([label, value]) =>
|
|
763
|
-
`${label}: ${value}`
|
|
764
|
-
.split('\n')
|
|
765
|
-
.map((line) => `> ${line}`)
|
|
766
|
-
.join('\n'),
|
|
767
|
-
)
|
|
768
|
-
.join('\n');
|
|
769
|
-
}
|
|
581
|
+
const RELAY_TOKEN = /<[a-z][a-z0-9-]*>/g;
|
|
770
582
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
]
|
|
583
|
+
// Quoted-relay composition: an empty value contributes no line, every
|
|
584
|
+
// continuation line of a multi-line value is quoted with `> ` (keeping LF/CRLF
|
|
585
|
+
// separators), and no quoted line is inserted. Substitution is single-pass and
|
|
586
|
+
// literal; a missing value keeps its placeholder literal.
|
|
587
|
+
function composeRelay(
|
|
588
|
+
template: readonly string[],
|
|
589
|
+
values: Readonly<Record<string, string | undefined>>,
|
|
590
|
+
): string {
|
|
591
|
+
const lines: string[] = [];
|
|
592
|
+
for (const line of template) {
|
|
593
|
+
const tokens = line.match(RELAY_TOKEN) ?? [];
|
|
594
|
+
if (tokens.some((token) => values[token] === '')) continue;
|
|
595
|
+
const composed = line.replace(RELAY_TOKEN, (token) => {
|
|
596
|
+
const value = values[token];
|
|
597
|
+
return value === undefined
|
|
598
|
+
? token
|
|
599
|
+
: value.replace(/\r?\n/g, (separator) => `${separator}> `);
|
|
600
|
+
});
|
|
601
|
+
lines.push(composed);
|
|
602
|
+
}
|
|
603
|
+
return lines.join('\n');
|
|
777
604
|
}
|
|
778
605
|
|
|
779
|
-
function
|
|
780
|
-
|
|
606
|
+
function plannedCallValues(
|
|
607
|
+
context: DevContext,
|
|
608
|
+
): Readonly<Record<string, string | undefined>> {
|
|
609
|
+
return {
|
|
610
|
+
'<development-request>': context.developmentRequest,
|
|
611
|
+
'<discussion-context>': context.discussionContext,
|
|
612
|
+
'<planning-result>': context.planningResult,
|
|
613
|
+
};
|
|
781
614
|
}
|
|
782
615
|
|
|
783
|
-
function
|
|
784
|
-
return
|
|
616
|
+
function plannedCallText(context: DevContext): string {
|
|
617
|
+
return composeRelay(PLANNED_CALL_TEMPLATE, plannedCallValues(context));
|
|
785
618
|
}
|
|
786
619
|
|
|
787
620
|
function codeAfterDecideCallText(context: DevContext): string {
|
|
788
|
-
return
|
|
789
|
-
...
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
621
|
+
return composeRelay(CODE_AFTER_DECIDE_CALL_TEMPLATE, {
|
|
622
|
+
...plannedCallValues(context),
|
|
623
|
+
'<decide-commit>': context.decideCommit,
|
|
624
|
+
'<evaluated-revision>': context.evaluatedRevision,
|
|
625
|
+
});
|
|
793
626
|
}
|
|
794
627
|
|
|
795
|
-
function
|
|
796
|
-
return
|
|
628
|
+
function pullRequestCallText(context: DevContext): string {
|
|
629
|
+
return composeRelay(PULL_REQUEST_CALL_TEMPLATE, {
|
|
630
|
+
'<development-request>': context.developmentRequest,
|
|
631
|
+
'<issue-summary>': context.issueSummary,
|
|
632
|
+
'<branch>': context.branch,
|
|
633
|
+
'<base-revision>': context.baseRevision,
|
|
634
|
+
'<last-code-commit>': context.lastCodeCommit,
|
|
635
|
+
'<final-evaluated-revision>': context.finalEvaluatedRevision,
|
|
636
|
+
});
|
|
797
637
|
}
|
|
798
638
|
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
['Evaluated revision', context.finalEvaluatedRevision ?? ''],
|
|
809
|
-
]);
|
|
639
|
+
// ---------------------------------------------------------------------------
|
|
640
|
+
// Boss entry and reply surfaces.
|
|
641
|
+
|
|
642
|
+
function startsDev({ event }: { event: DevEvent }): boolean {
|
|
643
|
+
return (
|
|
644
|
+
event.type === 'START_DEV' &&
|
|
645
|
+
typeof event.developmentRequest === 'string' &&
|
|
646
|
+
event.developmentRequest.trim().length > 0
|
|
647
|
+
);
|
|
810
648
|
}
|
|
811
649
|
|
|
812
|
-
function
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
650
|
+
function emptyBossReply({ event }: { event: DevEvent }): boolean {
|
|
651
|
+
return (
|
|
652
|
+
event.type === 'BOSS_REPLY' &&
|
|
653
|
+
(typeof event.answer !== 'string' || event.answer.trim().length === 0)
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function resumableStates<const Id extends ResumableStateId>(
|
|
658
|
+
ids: readonly Id[],
|
|
816
659
|
) {
|
|
817
|
-
return {
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
660
|
+
return ids.map((id) => ({
|
|
661
|
+
guard: ({ context, event }: { context: DevContext; event: DevEvent }) =>
|
|
662
|
+
event.type === 'BOSS_REPLY' &&
|
|
663
|
+
typeof event.answer === 'string' &&
|
|
664
|
+
event.answer.trim().length > 0 &&
|
|
665
|
+
context.pendingBossQuestion?.resumeStateId === id &&
|
|
666
|
+
(event.questionId === undefined || event.questionId === id),
|
|
667
|
+
target: `#${id}` as const,
|
|
668
|
+
reenter: true as const,
|
|
669
|
+
actions: 'rememberBossReply' as const,
|
|
670
|
+
}));
|
|
825
671
|
}
|
|
826
672
|
|
|
827
673
|
const machineSetup = setup({
|
|
@@ -835,33 +681,31 @@ const machineSetup = setup({
|
|
|
835
681
|
player: fromPromise<PlayerOutput, PlayerInput>(async () => {
|
|
836
682
|
throw new Error('player actor must be provided by the runner');
|
|
837
683
|
}),
|
|
838
|
-
playbook: fromPromise<
|
|
684
|
+
playbook: fromPromise<PlaybookOutput, PlaybookInput>(async () => {
|
|
839
685
|
throw new Error('playbook actor must be provided by the runner');
|
|
840
686
|
}),
|
|
841
687
|
},
|
|
842
688
|
guards: {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
context.pendingBossQuestion?.resumeStateId === 'planAnalysis' &&
|
|
864
|
-
(event.questionId === undefined || event.questionId === 'planAnalysis'),
|
|
689
|
+
startsDev,
|
|
690
|
+
emptyBossReply,
|
|
691
|
+
discussionCompleteAfterBossReply,
|
|
692
|
+
codePlanned: plannedPath('code'),
|
|
693
|
+
decideThenCodePlanned: plannedPath('decideThenCode'),
|
|
694
|
+
codeViaPullRequestPlanned: plannedPath('codeViaPullRequest'),
|
|
695
|
+
decideThenCodeViaPullRequestPlanned: plannedPath(
|
|
696
|
+
'decideThenCodeViaPullRequest',
|
|
697
|
+
),
|
|
698
|
+
needsBossReplyWithQuestion,
|
|
699
|
+
needsBossReplyWithoutQuestion,
|
|
700
|
+
branchSucceededForCode: branchSucceededFor('code'),
|
|
701
|
+
branchSucceededForDecide: branchSucceededFor('decide-then-code'),
|
|
702
|
+
decideSucceeded,
|
|
703
|
+
codeSucceededOnPullRequestPath,
|
|
704
|
+
codeSucceededOnPlainPath,
|
|
705
|
+
authoredBranchResult: authoredResultFrom('branch'),
|
|
706
|
+
authoredDecideResult: authoredResultFrom('decide'),
|
|
707
|
+
authoredCodeResult: authoredResultFrom('code'),
|
|
708
|
+
authoredPrResult: authoredResultFrom('pr'),
|
|
865
709
|
},
|
|
866
710
|
actions: {
|
|
867
711
|
'playbook.acceptedOutcome': (
|
|
@@ -872,173 +716,154 @@ const machineSetup = setup({
|
|
|
872
716
|
readonly acceptedOutcome: string;
|
|
873
717
|
},
|
|
874
718
|
) => undefined,
|
|
875
|
-
startDev: assign(({
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
childFailure: undefined,
|
|
894
|
-
lastError: undefined,
|
|
895
|
-
pendingBossQuestion: undefined,
|
|
896
|
-
bossReply: undefined,
|
|
897
|
-
};
|
|
898
|
-
}),
|
|
719
|
+
startDev: assign(({ event }) => ({
|
|
720
|
+
developmentRequest:
|
|
721
|
+
event.type === 'START_DEV' ? event.developmentRequest : undefined,
|
|
722
|
+
discussionContext: '',
|
|
723
|
+
planningResult: undefined,
|
|
724
|
+
pullRequestPath: undefined,
|
|
725
|
+
decideCommit: undefined,
|
|
726
|
+
evaluatedRevision: undefined,
|
|
727
|
+
branch: undefined,
|
|
728
|
+
baseRevision: undefined,
|
|
729
|
+
issueSummary: undefined,
|
|
730
|
+
lastCodeCommit: undefined,
|
|
731
|
+
finalEvaluatedRevision: undefined,
|
|
732
|
+
completion: undefined,
|
|
733
|
+
lastError: undefined,
|
|
734
|
+
pendingBossQuestion: undefined,
|
|
735
|
+
bossReply: undefined,
|
|
736
|
+
})),
|
|
899
737
|
completeDiscussion: assign(({ context }) => ({
|
|
900
|
-
|
|
901
|
-
completion: 'discussion-complete' as const,
|
|
738
|
+
discussionContext: archivedDiscussion(context),
|
|
739
|
+
completion: { kind: 'discussion-complete' } as const,
|
|
902
740
|
pendingBossQuestion: undefined,
|
|
903
741
|
bossReply: undefined,
|
|
904
742
|
})),
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
),
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
743
|
+
acceptPlainPlan: assign(({ context, event }) => ({
|
|
744
|
+
planningResult: outputText(event, 'planningResult'),
|
|
745
|
+
discussionContext: archivedDiscussion(context),
|
|
746
|
+
pendingBossQuestion: undefined,
|
|
747
|
+
bossReply: undefined,
|
|
748
|
+
})),
|
|
749
|
+
acceptPullRequestPlan: assign(
|
|
750
|
+
({ context, event }, params: { readonly path: DevPullRequestPath }) => ({
|
|
751
|
+
planningResult: outputText(event, 'planningResult'),
|
|
752
|
+
pullRequestPath: params.path,
|
|
753
|
+
discussionContext: archivedDiscussion(context),
|
|
754
|
+
pendingBossQuestion: undefined,
|
|
755
|
+
bossReply: undefined,
|
|
756
|
+
}),
|
|
916
757
|
),
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
return { lastError: new Error('Analyst question output was malformed') };
|
|
921
|
-
}
|
|
922
|
-
return {
|
|
923
|
-
discussionExchanges: archivedExchanges(context),
|
|
758
|
+
setPendingBossQuestion: assign(
|
|
759
|
+
({ context, event }, params: PendingBossQuestionParams) => ({
|
|
760
|
+
discussionContext: archivedDiscussion(context),
|
|
924
761
|
pendingBossQuestion: {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
asker: { kind: 'role' as const, roleId: 'analyst' as const },
|
|
929
|
-
question,
|
|
762
|
+
...params,
|
|
763
|
+
questionId: params.resumeStateId,
|
|
764
|
+
question: outputText(event, 'question') ?? '',
|
|
930
765
|
},
|
|
931
766
|
bossReply: undefined,
|
|
932
|
-
}
|
|
933
|
-
|
|
767
|
+
}),
|
|
768
|
+
),
|
|
934
769
|
rememberBossReply: assign(({ event }) =>
|
|
935
770
|
event.type === 'BOSS_REPLY' ? { bossReply: event.answer } : {},
|
|
936
771
|
),
|
|
937
|
-
|
|
938
|
-
lastError: () => new Error('BOSS_REPLY received an empty answer'),
|
|
772
|
+
clearBossReplyContext: assign({
|
|
939
773
|
pendingBossQuestion: undefined,
|
|
940
774
|
bossReply: undefined,
|
|
941
775
|
}),
|
|
942
776
|
rememberBranchResult: assign(({ event }) => {
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
return {
|
|
946
|
-
branch: output.branch,
|
|
947
|
-
baseRevision: output.baseRevision,
|
|
948
|
-
issueSummary: output.issueSummary,
|
|
949
|
-
};
|
|
777
|
+
const success = branchSuccessOf(childOutputOf(event));
|
|
778
|
+
return success === undefined ? {} : { ...success };
|
|
950
779
|
}),
|
|
951
780
|
rememberDecideResult: assign(({ event }) => {
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
return {
|
|
955
|
-
decideCommit: output.decideCommit,
|
|
956
|
-
evaluatedRevision: output.evaluatedRevision,
|
|
957
|
-
};
|
|
781
|
+
const success = decideSuccessOf(childOutputOf(event));
|
|
782
|
+
return success === undefined ? {} : { ...success };
|
|
958
783
|
}),
|
|
959
784
|
rememberCodeResult: assign(({ event }) => {
|
|
960
|
-
const
|
|
961
|
-
|
|
962
|
-
return {
|
|
963
|
-
lastCodeCommit: output.lastCodeCommit,
|
|
964
|
-
finalEvaluatedRevision: output.finalEvaluatedRevision,
|
|
965
|
-
};
|
|
785
|
+
const success = codeSuccessOf(childOutputOf(event));
|
|
786
|
+
return success === undefined ? {} : { ...success };
|
|
966
787
|
}),
|
|
967
|
-
completeWithChildSuccess: assign(
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
completion: 'child-failed' as const,
|
|
996
|
-
childFailure: relayedChildFailure(event, 'decide'),
|
|
997
|
-
})),
|
|
998
|
-
completeWithPrFailure: assign(({ event }) => ({
|
|
999
|
-
completion: 'child-failed' as const,
|
|
1000
|
-
childFailure: relayedChildFailure(event, 'pr'),
|
|
1001
|
-
})),
|
|
788
|
+
completeWithChildSuccess: assign(
|
|
789
|
+
({ event }, params: { readonly childPlaybookId: 'code' | 'pr' }) => {
|
|
790
|
+
const output = childOutputOf(event);
|
|
791
|
+
return {
|
|
792
|
+
completion: {
|
|
793
|
+
kind: 'complete',
|
|
794
|
+
childPlaybookId: params.childPlaybookId,
|
|
795
|
+
...(output === undefined ? {} : { childOutput: output }),
|
|
796
|
+
} as const,
|
|
797
|
+
};
|
|
798
|
+
},
|
|
799
|
+
),
|
|
800
|
+
completeWithInsufficientResult: assign(
|
|
801
|
+
({ event }, params: { readonly playbookId: DevChildPlaybookId }) => ({
|
|
802
|
+
completion: {
|
|
803
|
+
kind: 'child-failed',
|
|
804
|
+
childResult: insufficientChildResult(event, params.playbookId),
|
|
805
|
+
} as const,
|
|
806
|
+
}),
|
|
807
|
+
),
|
|
808
|
+
completeWithAuthoredChildResult: assign(
|
|
809
|
+
({ event }, params: { readonly playbookId: DevChildPlaybookId }) => {
|
|
810
|
+
const childResult = relayedChildResult(event, params.playbookId);
|
|
811
|
+
return childResult === undefined
|
|
812
|
+
? {}
|
|
813
|
+
: { completion: { kind: 'child-failed', childResult } as const };
|
|
814
|
+
},
|
|
815
|
+
),
|
|
1002
816
|
rememberActorError: assign(({ event }) => ({
|
|
1003
|
-
lastError: (event
|
|
817
|
+
lastError: normalizedControlError(childErrorOf(event)),
|
|
1004
818
|
})),
|
|
1005
819
|
rememberMalformedPlayerOutput: assign({
|
|
1006
|
-
lastError:
|
|
1007
|
-
|
|
820
|
+
lastError: {
|
|
821
|
+
name: 'MalformedActorOutput',
|
|
822
|
+
message:
|
|
823
|
+
'Analyst output for DEV-1 did not match an available declared result.',
|
|
824
|
+
},
|
|
825
|
+
pendingBossQuestion: undefined,
|
|
826
|
+
bossReply: undefined,
|
|
827
|
+
}),
|
|
828
|
+
rememberMalformedBossReply: assign({
|
|
829
|
+
lastError: {
|
|
830
|
+
name: 'MalformedBossReply',
|
|
831
|
+
message: 'BOSS_REPLY carried an empty or whitespace-only answer.',
|
|
832
|
+
},
|
|
833
|
+
pendingBossQuestion: undefined,
|
|
834
|
+
bossReply: undefined,
|
|
1008
835
|
}),
|
|
1009
836
|
},
|
|
1010
837
|
});
|
|
1011
838
|
|
|
1012
839
|
export const devMachine = machineSetup.createMachine({
|
|
1013
840
|
id: 'dev',
|
|
841
|
+
description:
|
|
842
|
+
'Plans the development path for a request and runs the child playbooks it selects.',
|
|
1014
843
|
initial: 'ready',
|
|
1015
|
-
context: ({ input }) => ({
|
|
1016
|
-
runResults: typeof input
|
|
1017
|
-
|
|
1018
|
-
deliveryViaPullRequest: false,
|
|
844
|
+
context: ({ input }): DevContext => ({
|
|
845
|
+
runResults: typeof input?.runResults === 'string' ? input.runResults : '',
|
|
846
|
+
discussionContext: '',
|
|
1019
847
|
}),
|
|
1020
848
|
output: ({ context }): DevPlaybookOutput => {
|
|
1021
|
-
|
|
1022
|
-
|
|
849
|
+
const completion = context.completion;
|
|
850
|
+
if (completion === undefined) {
|
|
851
|
+
throw new Error('DEV reached a final state without a recorded completion');
|
|
1023
852
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
status: 'complete'
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
throw new Error('DEV reached a final state without a child result');
|
|
1038
|
-
}
|
|
1039
|
-
return { status: 'child-failed', childResult: context.childFailure };
|
|
853
|
+
switch (completion.kind) {
|
|
854
|
+
case 'discussion-complete':
|
|
855
|
+
return { status: 'discussion-complete' };
|
|
856
|
+
case 'complete':
|
|
857
|
+
return {
|
|
858
|
+
status: 'complete',
|
|
859
|
+
childPlaybookId: completion.childPlaybookId,
|
|
860
|
+
...(completion.childOutput === undefined
|
|
861
|
+
? {}
|
|
862
|
+
: { childOutput: completion.childOutput }),
|
|
863
|
+
};
|
|
864
|
+
case 'child-failed':
|
|
865
|
+
return { status: 'child-failed', childResult: completion.childResult };
|
|
1040
866
|
}
|
|
1041
|
-
throw new Error('DEV reached a final state without a recorded completion');
|
|
1042
867
|
},
|
|
1043
868
|
states: {
|
|
1044
869
|
ready: {
|
|
@@ -1047,13 +872,17 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1047
872
|
meta: playbookMeta('ready'),
|
|
1048
873
|
tags: ['playbook.parked'],
|
|
1049
874
|
on: {
|
|
1050
|
-
START_DEV: {
|
|
875
|
+
START_DEV: {
|
|
876
|
+
guard: 'startsDev',
|
|
877
|
+
target: 'planAnalysis',
|
|
878
|
+
actions: 'startDev',
|
|
879
|
+
},
|
|
1051
880
|
},
|
|
1052
881
|
},
|
|
1053
882
|
planAnalysis: {
|
|
1054
883
|
id: 'planAnalysis',
|
|
1055
884
|
description: STATE_DESCRIPTIONS.planAnalysis,
|
|
1056
|
-
meta:
|
|
885
|
+
meta: roleMeta('planAnalysis', 'analyst'),
|
|
1057
886
|
tags: ['playbook.busy'],
|
|
1058
887
|
invoke: {
|
|
1059
888
|
src: 'player',
|
|
@@ -1062,11 +891,9 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1062
891
|
role: 'analyst',
|
|
1063
892
|
sourceItem: 'DEV-1',
|
|
1064
893
|
prompt: PLAN_ANALYSIS_PROMPT,
|
|
1065
|
-
result:
|
|
894
|
+
result: PLAN_ANALYSIS_RESULT,
|
|
1066
895
|
developmentRequest: context.developmentRequest ?? '',
|
|
1067
|
-
discussionContext:
|
|
1068
|
-
context.discussionExchanges,
|
|
1069
|
-
),
|
|
896
|
+
discussionContext: context.discussionContext,
|
|
1070
897
|
runResults: context.runResults,
|
|
1071
898
|
...(context.pendingBossQuestion === undefined
|
|
1072
899
|
? {}
|
|
@@ -1077,7 +904,27 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1077
904
|
}),
|
|
1078
905
|
onDone: [
|
|
1079
906
|
{
|
|
1080
|
-
guard: '
|
|
907
|
+
guard: 'needsBossReplyWithQuestion',
|
|
908
|
+
target: 'awaitBossReply',
|
|
909
|
+
actions: [
|
|
910
|
+
{
|
|
911
|
+
type: 'playbook.acceptedOutcome',
|
|
912
|
+
params: {
|
|
913
|
+
source: 'planAnalysis',
|
|
914
|
+
target: 'awaitBossReply',
|
|
915
|
+
acceptedOutcome: 'needsBossReply',
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
{ type: 'setPendingBossQuestion', params: PLAN_ANALYSIS_QUESTION },
|
|
919
|
+
],
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
guard: 'needsBossReplyWithoutQuestion',
|
|
923
|
+
target: 'failed',
|
|
924
|
+
actions: 'rememberMalformedPlayerOutput',
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
guard: 'discussionCompleteAfterBossReply',
|
|
1081
928
|
target: 'discussionComplete',
|
|
1082
929
|
actions: [
|
|
1083
930
|
{
|
|
@@ -1092,7 +939,7 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1092
939
|
],
|
|
1093
940
|
},
|
|
1094
941
|
{
|
|
1095
|
-
guard: '
|
|
942
|
+
guard: 'codePlanned',
|
|
1096
943
|
target: 'callCode',
|
|
1097
944
|
actions: [
|
|
1098
945
|
{
|
|
@@ -1103,11 +950,11 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1103
950
|
acceptedOutcome: 'code',
|
|
1104
951
|
},
|
|
1105
952
|
},
|
|
1106
|
-
'
|
|
953
|
+
'acceptPlainPlan',
|
|
1107
954
|
],
|
|
1108
955
|
},
|
|
1109
956
|
{
|
|
1110
|
-
guard: '
|
|
957
|
+
guard: 'decideThenCodePlanned',
|
|
1111
958
|
target: 'callDecide',
|
|
1112
959
|
actions: [
|
|
1113
960
|
{
|
|
@@ -1118,11 +965,11 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1118
965
|
acceptedOutcome: 'decideThenCode',
|
|
1119
966
|
},
|
|
1120
967
|
},
|
|
1121
|
-
'
|
|
968
|
+
'acceptPlainPlan',
|
|
1122
969
|
],
|
|
1123
970
|
},
|
|
1124
971
|
{
|
|
1125
|
-
guard: '
|
|
972
|
+
guard: 'codeViaPullRequestPlanned',
|
|
1126
973
|
target: 'createBranch',
|
|
1127
974
|
actions: [
|
|
1128
975
|
{
|
|
@@ -1133,11 +980,11 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1133
980
|
acceptedOutcome: 'codeViaPullRequest',
|
|
1134
981
|
},
|
|
1135
982
|
},
|
|
1136
|
-
'
|
|
983
|
+
{ type: 'acceptPullRequestPlan', params: { path: 'code' } },
|
|
1137
984
|
],
|
|
1138
985
|
},
|
|
1139
986
|
{
|
|
1140
|
-
guard: '
|
|
987
|
+
guard: 'decideThenCodeViaPullRequestPlanned',
|
|
1141
988
|
target: 'createBranch',
|
|
1142
989
|
actions: [
|
|
1143
990
|
{
|
|
@@ -1148,22 +995,10 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1148
995
|
acceptedOutcome: 'decideThenCodeViaPullRequest',
|
|
1149
996
|
},
|
|
1150
997
|
},
|
|
1151
|
-
'rememberDecideThenCodeViaPullRequestPath',
|
|
1152
|
-
],
|
|
1153
|
-
},
|
|
1154
|
-
{
|
|
1155
|
-
guard: 'needsBossReply',
|
|
1156
|
-
target: 'awaitBossReply',
|
|
1157
|
-
actions: [
|
|
1158
998
|
{
|
|
1159
|
-
type: '
|
|
1160
|
-
params: {
|
|
1161
|
-
source: 'planAnalysis',
|
|
1162
|
-
target: 'awaitBossReply',
|
|
1163
|
-
acceptedOutcome: 'needsBossReply',
|
|
1164
|
-
},
|
|
999
|
+
type: 'acceptPullRequestPlan',
|
|
1000
|
+
params: { path: 'decide-then-code' },
|
|
1165
1001
|
},
|
|
1166
|
-
'rememberPendingQuestion',
|
|
1167
1002
|
],
|
|
1168
1003
|
},
|
|
1169
1004
|
{
|
|
@@ -1171,7 +1006,31 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1171
1006
|
actions: 'rememberMalformedPlayerOutput',
|
|
1172
1007
|
},
|
|
1173
1008
|
],
|
|
1174
|
-
onError: {
|
|
1009
|
+
onError: {
|
|
1010
|
+
target: 'failed',
|
|
1011
|
+
actions: ['rememberActorError', 'clearBossReplyContext'],
|
|
1012
|
+
},
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
awaitBossReply: {
|
|
1016
|
+
id: 'awaitBossReply',
|
|
1017
|
+
description: STATE_DESCRIPTIONS.awaitBossReply,
|
|
1018
|
+
meta: playbookMeta('awaitBossReply'),
|
|
1019
|
+
tags: ['playbook.parked'],
|
|
1020
|
+
on: {
|
|
1021
|
+
BOSS_REPLY: [
|
|
1022
|
+
{
|
|
1023
|
+
guard: 'emptyBossReply',
|
|
1024
|
+
target: 'failed',
|
|
1025
|
+
actions: 'rememberMalformedBossReply',
|
|
1026
|
+
},
|
|
1027
|
+
...resumableStates(['planAnalysis']),
|
|
1028
|
+
],
|
|
1029
|
+
START_DEV: {
|
|
1030
|
+
guard: 'startsDev',
|
|
1031
|
+
target: 'planAnalysis',
|
|
1032
|
+
actions: 'startDev',
|
|
1033
|
+
},
|
|
1175
1034
|
},
|
|
1176
1035
|
},
|
|
1177
1036
|
createBranch: {
|
|
@@ -1185,29 +1044,35 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1185
1044
|
stateId: 'createBranch',
|
|
1186
1045
|
sourceItem: 'DEV-5',
|
|
1187
1046
|
playbookId: 'branch',
|
|
1188
|
-
text:
|
|
1047
|
+
text: plannedCallText(context),
|
|
1189
1048
|
}),
|
|
1190
1049
|
onDone: [
|
|
1191
1050
|
{
|
|
1192
|
-
guard: '
|
|
1051
|
+
guard: 'branchSucceededForCode',
|
|
1193
1052
|
target: 'callCode',
|
|
1194
1053
|
actions: 'rememberBranchResult',
|
|
1195
1054
|
},
|
|
1196
1055
|
{
|
|
1197
|
-
guard: '
|
|
1056
|
+
guard: 'branchSucceededForDecide',
|
|
1198
1057
|
target: 'callDecide',
|
|
1199
1058
|
actions: 'rememberBranchResult',
|
|
1200
1059
|
},
|
|
1201
1060
|
{
|
|
1202
1061
|
target: 'reportedChildFailure',
|
|
1203
|
-
actions:
|
|
1062
|
+
actions: {
|
|
1063
|
+
type: 'completeWithInsufficientResult',
|
|
1064
|
+
params: { playbookId: 'branch' },
|
|
1065
|
+
},
|
|
1204
1066
|
},
|
|
1205
1067
|
],
|
|
1206
1068
|
onError: [
|
|
1207
1069
|
{
|
|
1208
|
-
guard: '
|
|
1070
|
+
guard: 'authoredBranchResult',
|
|
1209
1071
|
target: 'reportedChildFailure',
|
|
1210
|
-
actions:
|
|
1072
|
+
actions: {
|
|
1073
|
+
type: 'completeWithAuthoredChildResult',
|
|
1074
|
+
params: { playbookId: 'branch' },
|
|
1075
|
+
},
|
|
1211
1076
|
},
|
|
1212
1077
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
1213
1078
|
],
|
|
@@ -1224,29 +1089,38 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1224
1089
|
stateId: 'callCode',
|
|
1225
1090
|
sourceItem: 'DEV-2',
|
|
1226
1091
|
playbookId: 'code',
|
|
1227
|
-
text:
|
|
1092
|
+
text: plannedCallText(context),
|
|
1228
1093
|
}),
|
|
1229
1094
|
onDone: [
|
|
1230
1095
|
{
|
|
1231
|
-
guard: '
|
|
1096
|
+
guard: 'codeSucceededOnPullRequestPath',
|
|
1232
1097
|
target: 'openPullRequest',
|
|
1233
1098
|
actions: 'rememberCodeResult',
|
|
1234
1099
|
},
|
|
1235
1100
|
{
|
|
1236
|
-
guard: '
|
|
1101
|
+
guard: 'codeSucceededOnPlainPath',
|
|
1237
1102
|
target: 'done',
|
|
1238
|
-
actions:
|
|
1103
|
+
actions: {
|
|
1104
|
+
type: 'completeWithChildSuccess',
|
|
1105
|
+
params: { childPlaybookId: 'code' },
|
|
1106
|
+
},
|
|
1239
1107
|
},
|
|
1240
1108
|
{
|
|
1241
1109
|
target: 'reportedChildFailure',
|
|
1242
|
-
actions:
|
|
1110
|
+
actions: {
|
|
1111
|
+
type: 'completeWithInsufficientResult',
|
|
1112
|
+
params: { playbookId: 'code' },
|
|
1113
|
+
},
|
|
1243
1114
|
},
|
|
1244
1115
|
],
|
|
1245
1116
|
onError: [
|
|
1246
1117
|
{
|
|
1247
|
-
guard: '
|
|
1118
|
+
guard: 'authoredCodeResult',
|
|
1248
1119
|
target: 'reportedChildFailure',
|
|
1249
|
-
actions:
|
|
1120
|
+
actions: {
|
|
1121
|
+
type: 'completeWithAuthoredChildResult',
|
|
1122
|
+
params: { playbookId: 'code' },
|
|
1123
|
+
},
|
|
1250
1124
|
},
|
|
1251
1125
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
1252
1126
|
],
|
|
@@ -1263,24 +1137,30 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1263
1137
|
stateId: 'callDecide',
|
|
1264
1138
|
sourceItem: 'DEV-3',
|
|
1265
1139
|
playbookId: 'decide',
|
|
1266
|
-
text:
|
|
1140
|
+
text: plannedCallText(context),
|
|
1267
1141
|
}),
|
|
1268
1142
|
onDone: [
|
|
1269
1143
|
{
|
|
1270
|
-
guard: '
|
|
1144
|
+
guard: 'decideSucceeded',
|
|
1271
1145
|
target: 'callCodeAfterDecide',
|
|
1272
1146
|
actions: 'rememberDecideResult',
|
|
1273
1147
|
},
|
|
1274
1148
|
{
|
|
1275
1149
|
target: 'reportedChildFailure',
|
|
1276
|
-
actions:
|
|
1150
|
+
actions: {
|
|
1151
|
+
type: 'completeWithInsufficientResult',
|
|
1152
|
+
params: { playbookId: 'decide' },
|
|
1153
|
+
},
|
|
1277
1154
|
},
|
|
1278
1155
|
],
|
|
1279
1156
|
onError: [
|
|
1280
1157
|
{
|
|
1281
|
-
guard: '
|
|
1158
|
+
guard: 'authoredDecideResult',
|
|
1282
1159
|
target: 'reportedChildFailure',
|
|
1283
|
-
actions:
|
|
1160
|
+
actions: {
|
|
1161
|
+
type: 'completeWithAuthoredChildResult',
|
|
1162
|
+
params: { playbookId: 'decide' },
|
|
1163
|
+
},
|
|
1284
1164
|
},
|
|
1285
1165
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
1286
1166
|
],
|
|
@@ -1301,25 +1181,34 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1301
1181
|
}),
|
|
1302
1182
|
onDone: [
|
|
1303
1183
|
{
|
|
1304
|
-
guard: '
|
|
1184
|
+
guard: 'codeSucceededOnPullRequestPath',
|
|
1305
1185
|
target: 'openPullRequest',
|
|
1306
1186
|
actions: 'rememberCodeResult',
|
|
1307
1187
|
},
|
|
1308
1188
|
{
|
|
1309
|
-
guard: '
|
|
1189
|
+
guard: 'codeSucceededOnPlainPath',
|
|
1310
1190
|
target: 'done',
|
|
1311
|
-
actions:
|
|
1191
|
+
actions: {
|
|
1192
|
+
type: 'completeWithChildSuccess',
|
|
1193
|
+
params: { childPlaybookId: 'code' },
|
|
1194
|
+
},
|
|
1312
1195
|
},
|
|
1313
1196
|
{
|
|
1314
1197
|
target: 'reportedChildFailure',
|
|
1315
|
-
actions:
|
|
1198
|
+
actions: {
|
|
1199
|
+
type: 'completeWithInsufficientResult',
|
|
1200
|
+
params: { playbookId: 'code' },
|
|
1201
|
+
},
|
|
1316
1202
|
},
|
|
1317
1203
|
],
|
|
1318
1204
|
onError: [
|
|
1319
1205
|
{
|
|
1320
|
-
guard: '
|
|
1206
|
+
guard: 'authoredCodeResult',
|
|
1321
1207
|
target: 'reportedChildFailure',
|
|
1322
|
-
actions:
|
|
1208
|
+
actions: {
|
|
1209
|
+
type: 'completeWithAuthoredChildResult',
|
|
1210
|
+
params: { playbookId: 'code' },
|
|
1211
|
+
},
|
|
1323
1212
|
},
|
|
1324
1213
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
1325
1214
|
],
|
|
@@ -1338,67 +1227,58 @@ export const devMachine = machineSetup.createMachine({
|
|
|
1338
1227
|
playbookId: 'pr',
|
|
1339
1228
|
text: pullRequestCallText(context),
|
|
1340
1229
|
}),
|
|
1341
|
-
//
|
|
1342
|
-
//
|
|
1343
|
-
onDone: {
|
|
1230
|
+
// `pr` success completes DEV with that result; a `pr` failure
|
|
1231
|
+
// terminal arrives through `onError` and is relayed below.
|
|
1232
|
+
onDone: {
|
|
1233
|
+
target: 'done',
|
|
1234
|
+
actions: {
|
|
1235
|
+
type: 'completeWithChildSuccess',
|
|
1236
|
+
params: { childPlaybookId: 'pr' },
|
|
1237
|
+
},
|
|
1238
|
+
},
|
|
1344
1239
|
onError: [
|
|
1345
1240
|
{
|
|
1346
|
-
guard: '
|
|
1241
|
+
guard: 'authoredPrResult',
|
|
1347
1242
|
target: 'reportedChildFailure',
|
|
1348
|
-
actions:
|
|
1243
|
+
actions: {
|
|
1244
|
+
type: 'completeWithAuthoredChildResult',
|
|
1245
|
+
params: { playbookId: 'pr' },
|
|
1246
|
+
},
|
|
1349
1247
|
},
|
|
1350
1248
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
1351
1249
|
],
|
|
1352
1250
|
},
|
|
1353
1251
|
},
|
|
1354
|
-
awaitBossReply: {
|
|
1355
|
-
id: 'awaitBossReply',
|
|
1356
|
-
description: STATE_DESCRIPTIONS.awaitBossReply,
|
|
1357
|
-
meta: playbookMeta('awaitBossReply'),
|
|
1358
|
-
tags: ['playbook.parked'],
|
|
1359
|
-
on: {
|
|
1360
|
-
BOSS_REPLY: [
|
|
1361
|
-
{
|
|
1362
|
-
guard: 'emptyBossReply',
|
|
1363
|
-
target: '#failed',
|
|
1364
|
-
actions: 'rememberEmptyBossReplyError',
|
|
1365
|
-
},
|
|
1366
|
-
{
|
|
1367
|
-
guard: 'resumesPlanAnalysis',
|
|
1368
|
-
target: '#planAnalysis',
|
|
1369
|
-
reenter: true,
|
|
1370
|
-
actions: 'rememberBossReply',
|
|
1371
|
-
},
|
|
1372
|
-
],
|
|
1373
|
-
START_DEV: { target: 'planAnalysis', actions: 'startDev' },
|
|
1374
|
-
},
|
|
1375
|
-
},
|
|
1376
1252
|
failed: {
|
|
1377
1253
|
id: 'failed',
|
|
1378
1254
|
description: STATE_DESCRIPTIONS.failed,
|
|
1379
1255
|
meta: playbookMeta('failed'),
|
|
1380
1256
|
tags: ['playbook.parked'],
|
|
1381
1257
|
on: {
|
|
1382
|
-
START_DEV: {
|
|
1258
|
+
START_DEV: {
|
|
1259
|
+
guard: 'startsDev',
|
|
1260
|
+
target: 'planAnalysis',
|
|
1261
|
+
actions: 'startDev',
|
|
1262
|
+
},
|
|
1383
1263
|
},
|
|
1384
1264
|
},
|
|
1385
1265
|
discussionComplete: {
|
|
1386
1266
|
id: 'discussionComplete',
|
|
1267
|
+
type: 'final',
|
|
1387
1268
|
description: STATE_DESCRIPTIONS.discussionComplete,
|
|
1388
1269
|
meta: terminalMeta('discussionComplete', 'success'),
|
|
1389
|
-
type: 'final',
|
|
1390
1270
|
},
|
|
1391
1271
|
done: {
|
|
1392
1272
|
id: 'done',
|
|
1273
|
+
type: 'final',
|
|
1393
1274
|
description: STATE_DESCRIPTIONS.done,
|
|
1394
1275
|
meta: terminalMeta('done', 'success'),
|
|
1395
|
-
type: 'final',
|
|
1396
1276
|
},
|
|
1397
1277
|
reportedChildFailure: {
|
|
1398
1278
|
id: 'reportedChildFailure',
|
|
1279
|
+
type: 'final',
|
|
1399
1280
|
description: STATE_DESCRIPTIONS.reportedChildFailure,
|
|
1400
1281
|
meta: terminalMeta('reportedChildFailure', 'failure'),
|
|
1401
|
-
type: 'final',
|
|
1402
1282
|
},
|
|
1403
1283
|
},
|
|
1404
1284
|
});
|