@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,13 +1,15 @@
|
|
|
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
|
import { assign, fromPromise, setup } from 'xstate';
|
|
8
|
+
import { validatePlaybookCallResult } from '@sublang/playbook/xstate-runtime';
|
|
9
|
+
/** DEV declares no parallel group. */
|
|
10
|
+
export const concurrentRoleSets = [];
|
|
11
|
+
// DEV-1: the item's blockquote, verbatim, with the outer GEARS marker removed.
|
|
6
12
|
const PLAN_ANALYSIS_PROMPT = [
|
|
7
|
-
'> Original request: <development-request>',
|
|
8
|
-
'> Prior discussion: <discussion-context>',
|
|
9
|
-
'> Run results: <run-results>',
|
|
10
|
-
'',
|
|
11
13
|
'Plan which playbooks run for this request; the playbooks do the work.',
|
|
12
14
|
'Read the request, then the specs and the repository only as far as choosing the path requires.',
|
|
13
15
|
'Do not change files or commit while planning or discussing the request.',
|
|
@@ -26,50 +28,79 @@ const PLAN_ANALYSIS_PROMPT = [
|
|
|
26
28
|
'A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.',
|
|
27
29
|
'Do not choose `decide then code` merely because the work is large, nor `code` merely because it is small.',
|
|
28
30
|
'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
|
|
31
|
+
'',
|
|
32
|
+
'> Original request: <development-request>',
|
|
33
|
+
'> Prior discussion: <discussion-context>',
|
|
34
|
+
'> Run results: <run-results>',
|
|
29
35
|
].join('\n');
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
const NEEDS_BOSS_REPLY_DESCRIPTION = "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.";
|
|
37
|
+
// DEV-1 local result contract: the authored `Results:` bullets in declared
|
|
38
|
+
// order, plus the universal Boss-reply suspension.
|
|
39
|
+
const PLAN_ANALYSIS_RESULT = {
|
|
40
|
+
discussionComplete: '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.',
|
|
41
|
+
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>`.',
|
|
42
|
+
decideThenCode: '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>`.',
|
|
43
|
+
codeViaPullRequest: '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>`.',
|
|
44
|
+
decideThenCodeViaPullRequest: '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>`.',
|
|
45
|
+
needsBossReply: NEEDS_BOSS_REPLY_DESCRIPTION,
|
|
37
46
|
};
|
|
47
|
+
// DEV-2 and DEV-3 and DEV-5: identical composed blockquotes.
|
|
48
|
+
const PLANNED_CALL_TEMPLATE = [
|
|
49
|
+
'> Original request: <development-request>',
|
|
50
|
+
'> Prior discussion: <discussion-context>',
|
|
51
|
+
'> Planning result: <planning-result>',
|
|
52
|
+
];
|
|
53
|
+
// DEV-4.
|
|
54
|
+
const CODE_AFTER_DECIDE_CALL_TEMPLATE = [
|
|
55
|
+
'> Original request: <development-request>',
|
|
56
|
+
'> Prior discussion: <discussion-context>',
|
|
57
|
+
'> Planning result: <planning-result>',
|
|
58
|
+
'> DECIDE commit: <decide-commit>',
|
|
59
|
+
'> Evaluated revision: <evaluated-revision>',
|
|
60
|
+
];
|
|
61
|
+
// DEV-6.
|
|
62
|
+
const PULL_REQUEST_CALL_TEMPLATE = [
|
|
63
|
+
'> Original request: <development-request>',
|
|
64
|
+
'> Issue summary: <issue-summary>',
|
|
65
|
+
'> Branch: <branch>',
|
|
66
|
+
'> Base revision: <base-revision>',
|
|
67
|
+
'> CODE commit: <last-code-commit>',
|
|
68
|
+
'> Evaluated revision: <final-evaluated-revision>',
|
|
69
|
+
];
|
|
38
70
|
const STATE_DESCRIPTIONS = {
|
|
39
71
|
ready: 'Waiting for a development request.',
|
|
40
|
-
planAnalysis: 'Analyst
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
failed: '
|
|
48
|
-
discussionComplete: '
|
|
49
|
-
done: "The selected development path completed with
|
|
50
|
-
reportedChildFailure: "
|
|
72
|
+
planAnalysis: 'Analyst chooses the development path in a short planning note.',
|
|
73
|
+
awaitBossReply: "Waiting for Boss to answer the acting agent's question.",
|
|
74
|
+
createBranch: 'The BRANCH playbook prepares the branch for the pull-request delivery path.',
|
|
75
|
+
callCode: 'The CODE playbook implements the planned development path.',
|
|
76
|
+
callDecide: 'The DECIDE playbook settles the decision the planned development path requires.',
|
|
77
|
+
callCodeAfterDecide: 'The CODE playbook implements the development path DECIDE settled.',
|
|
78
|
+
openPullRequest: 'The PR playbook delivers the implemented branch through a pull request.',
|
|
79
|
+
failed: 'DEV failed outside a child result contract and waits for a new development request.',
|
|
80
|
+
discussionComplete: 'After a Boss reply, the planning discussion concluded with no child call or repository change.',
|
|
81
|
+
done: "The selected development path completed with its final child playbook's successful result.",
|
|
82
|
+
reportedChildFailure: "DEV ended by relaying a child playbook's authored abort, failure, or result that did not prove the required success.",
|
|
51
83
|
};
|
|
52
|
-
function playbookMeta(stateId
|
|
84
|
+
function playbookMeta(stateId) {
|
|
53
85
|
return {
|
|
54
|
-
playbook: {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
86
|
+
playbook: { stateId, description: STATE_DESCRIPTIONS[stateId] },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function roleMeta(stateId, role) {
|
|
90
|
+
return {
|
|
91
|
+
playbook: { stateId, description: STATE_DESCRIPTIONS[stateId], role },
|
|
59
92
|
};
|
|
60
93
|
}
|
|
61
|
-
// DR-048: a final state additionally declares whether its outcome means the
|
|
62
|
-
// workflow succeeded or failed, so DEV's own caller learns that from the
|
|
63
|
-
// machine rather than from DEV's output fields.
|
|
64
94
|
function terminalMeta(stateId, terminal) {
|
|
65
95
|
return {
|
|
66
|
-
playbook: {
|
|
67
|
-
stateId,
|
|
68
|
-
description: STATE_DESCRIPTIONS[stateId],
|
|
69
|
-
terminal,
|
|
70
|
-
},
|
|
96
|
+
playbook: { stateId, description: STATE_DESCRIPTIONS[stateId], terminal },
|
|
71
97
|
};
|
|
72
98
|
}
|
|
99
|
+
const PLAN_ANALYSIS_QUESTION = {
|
|
100
|
+
resumeStateId: 'planAnalysis',
|
|
101
|
+
sourceItem: 'DEV-1',
|
|
102
|
+
asker: { kind: 'role', roleId: 'analyst' },
|
|
103
|
+
};
|
|
73
104
|
function isRecord(value) {
|
|
74
105
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
75
106
|
return false;
|
|
@@ -80,364 +111,247 @@ function isRecord(value) {
|
|
|
80
111
|
function isNonEmptyString(value) {
|
|
81
112
|
return typeof value === 'string' && value.trim().length > 0;
|
|
82
113
|
}
|
|
83
|
-
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Player output (DEV-1), narrowed structurally from an unknown done event.
|
|
116
|
+
function playerOutputOf(event) {
|
|
84
117
|
if (!isRecord(event))
|
|
85
118
|
return undefined;
|
|
86
119
|
return isRecord(event.output) ? event.output : undefined;
|
|
87
120
|
}
|
|
88
|
-
function
|
|
89
|
-
return
|
|
121
|
+
function hasGuard(event, guard) {
|
|
122
|
+
return playerOutputOf(event)?.guard === guard;
|
|
90
123
|
}
|
|
91
|
-
function
|
|
92
|
-
const value =
|
|
124
|
+
function outputText(event, field) {
|
|
125
|
+
const value = playerOutputOf(event)?.[field];
|
|
93
126
|
return isNonEmptyString(value) ? value : undefined;
|
|
94
127
|
}
|
|
95
|
-
function
|
|
96
|
-
return (
|
|
97
|
-
outputString(event, 'planningResult') !== undefined);
|
|
98
|
-
}
|
|
99
|
-
function isDecideThenCode({ event }) {
|
|
100
|
-
return (outputGuard(event, 'decideThenCode') &&
|
|
101
|
-
outputString(event, 'planningResult') !== undefined);
|
|
128
|
+
function plannedPath(guard) {
|
|
129
|
+
return ({ event }) => hasGuard(event, guard) && outputText(event, 'planningResult') !== undefined;
|
|
102
130
|
}
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
function
|
|
106
|
-
return (
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
function isDecideThenCodeViaPullRequest({ event, }) {
|
|
110
|
-
return (outputGuard(event, 'decideThenCodeViaPullRequest') &&
|
|
111
|
-
outputString(event, 'planningResult') !== undefined);
|
|
112
|
-
}
|
|
113
|
-
// Discussion complete is available only after a Boss reply: the round that
|
|
114
|
-
// produced it must itself have been resumed with Boss's answer.
|
|
115
|
-
function isDiscussionComplete({ context, event, }) {
|
|
116
|
-
return (outputGuard(event, 'discussionComplete') &&
|
|
131
|
+
// Discussion complete is available only after a Boss reply: the Analyst
|
|
132
|
+
// invocation that returns it must have been resumed with Boss's answer.
|
|
133
|
+
function discussionCompleteAfterBossReply({ context, event, }) {
|
|
134
|
+
return (hasGuard(event, 'discussionComplete') &&
|
|
135
|
+
context.pendingBossQuestion !== undefined &&
|
|
117
136
|
isNonEmptyString(context.bossReply));
|
|
118
137
|
}
|
|
119
|
-
function
|
|
120
|
-
return (
|
|
121
|
-
|
|
138
|
+
function needsBossReplyWithQuestion({ event }) {
|
|
139
|
+
return (hasGuard(event, 'needsBossReply') && outputText(event, 'question') !== undefined);
|
|
140
|
+
}
|
|
141
|
+
function needsBossReplyWithoutQuestion({ event }) {
|
|
142
|
+
return (hasGuard(event, 'needsBossReply') && outputText(event, 'question') === undefined);
|
|
122
143
|
}
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Child outputs, read only from each child's canonical structured result and
|
|
146
|
+
// checked against the packaged public output interfaces.
|
|
123
147
|
function childOutputOf(event) {
|
|
124
148
|
return isRecord(event) ? event.output : undefined;
|
|
125
149
|
}
|
|
126
|
-
function
|
|
127
|
-
return
|
|
128
|
-
isNonEmptyString(value.decideCommit) &&
|
|
129
|
-
isNonEmptyString(value.evaluatedRevision) &&
|
|
130
|
-
value.noUnsettledFindings === true);
|
|
131
|
-
}
|
|
132
|
-
function isDecideSuccess({ event }) {
|
|
133
|
-
return isDecideSuccessValue(childOutputOf(event));
|
|
134
|
-
}
|
|
135
|
-
// BRANCH's canonical successful terminal output carries the exact name of
|
|
136
|
-
// the new checked-out branch, the exact receipt-owned base revision, and the
|
|
137
|
-
// issue summary; DEV consumes exactly those three by name and never defaults
|
|
138
|
-
// a missing one.
|
|
139
|
-
function isBranchSuccessValue(value) {
|
|
140
|
-
return (isRecord(value) &&
|
|
141
|
-
isNonEmptyString(value.branch) &&
|
|
142
|
-
isNonEmptyString(value.baseRevision) &&
|
|
143
|
-
isNonEmptyString(value.issueSummary));
|
|
144
|
-
}
|
|
145
|
-
function isBranchSuccessForCode({ context, event, }) {
|
|
146
|
-
return (context.pullRequestPath === 'code' &&
|
|
147
|
-
isBranchSuccessValue(childOutputOf(event)));
|
|
148
|
-
}
|
|
149
|
-
function isBranchSuccessForDecide({ context, event, }) {
|
|
150
|
-
return (context.pullRequestPath === 'decide-then-code' &&
|
|
151
|
-
isBranchSuccessValue(childOutputOf(event)));
|
|
152
|
-
}
|
|
153
|
-
// CODE's canonical successful terminal output discriminates completion with
|
|
154
|
-
// `status: 'complete'`, carries the exact last CODE-owned commit, and
|
|
155
|
-
// affirms that every phase's review passed.
|
|
156
|
-
function isCodeSuccessValue(value) {
|
|
157
|
-
return (isRecord(value) &&
|
|
158
|
-
value.status === 'complete' &&
|
|
159
|
-
isNonEmptyString(value.lastCodeCommit) &&
|
|
160
|
-
value.allReviewsPassed === true);
|
|
161
|
-
}
|
|
162
|
-
// A plain path completes DEV on that proof alone, exactly as before DR-050.
|
|
163
|
-
function isPlainCodeSuccess({ context, event, }) {
|
|
164
|
-
return (context.deliveryViaPullRequest !== true &&
|
|
165
|
-
isCodeSuccessValue(childOutputOf(event)));
|
|
166
|
-
}
|
|
167
|
-
// A pull-request path additionally consumes the exact final evaluated
|
|
168
|
-
// repository revision for the `pr` call, so a CODE result lacking it does not
|
|
169
|
-
// prove the success that path requires.
|
|
170
|
-
function isCodeSuccessValueForPullRequest(value) {
|
|
171
|
-
return (isCodeSuccessValue(value) &&
|
|
172
|
-
isRecord(value) &&
|
|
173
|
-
isNonEmptyString(value.finalEvaluatedRevision));
|
|
174
|
-
}
|
|
175
|
-
function isCodeSuccessViaPullRequest({ context, event, }) {
|
|
176
|
-
return (context.deliveryViaPullRequest === true &&
|
|
177
|
-
isCodeSuccessValueForPullRequest(childOutputOf(event)));
|
|
178
|
-
}
|
|
179
|
-
function isStateValue(value, ancestors = new Set()) {
|
|
180
|
-
if (typeof value === 'string')
|
|
181
|
-
return true;
|
|
182
|
-
if (!isRecord(value) || ancestors.has(value))
|
|
183
|
-
return false;
|
|
184
|
-
const next = new Set(ancestors).add(value);
|
|
185
|
-
return Reflect.ownKeys(value).every((key) => typeof key === 'string' &&
|
|
186
|
-
Object.prototype.propertyIsEnumerable.call(value, key) &&
|
|
187
|
-
isStateValue(value[key], next));
|
|
188
|
-
}
|
|
189
|
-
function isStringArray(value) {
|
|
190
|
-
return (Array.isArray(value) &&
|
|
191
|
-
Reflect.ownKeys(value).length === value.length + 1 &&
|
|
192
|
-
Reflect.ownKeys(value).every((key) => {
|
|
193
|
-
if (key === 'length')
|
|
194
|
-
return true;
|
|
195
|
-
return (typeof key === 'string' &&
|
|
196
|
-
/^(0|[1-9][0-9]*)$/.test(key) &&
|
|
197
|
-
Number(key) < value.length);
|
|
198
|
-
}) &&
|
|
199
|
-
value.every((entry) => isNonEmptyString(entry)) &&
|
|
200
|
-
new Set(value).size === value.length);
|
|
201
|
-
}
|
|
202
|
-
function isPlaybookState(value) {
|
|
203
|
-
if (!isRecord(value))
|
|
204
|
-
return false;
|
|
205
|
-
const allowed = new Set([
|
|
206
|
-
'value',
|
|
207
|
-
'activeStateIds',
|
|
208
|
-
'tags',
|
|
209
|
-
'status',
|
|
210
|
-
'quiescent',
|
|
211
|
-
'stateId',
|
|
212
|
-
]);
|
|
213
|
-
if (Reflect.ownKeys(value).some((key) => typeof key !== 'string' || !allowed.has(key)) ||
|
|
214
|
-
!Object.prototype.hasOwnProperty.call(value, 'value') ||
|
|
215
|
-
!Object.prototype.hasOwnProperty.call(value, 'activeStateIds') ||
|
|
216
|
-
!Object.prototype.hasOwnProperty.call(value, 'tags') ||
|
|
217
|
-
!Object.prototype.hasOwnProperty.call(value, 'status') ||
|
|
218
|
-
!Object.prototype.hasOwnProperty.call(value, 'quiescent')) {
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
return (isStateValue(value.value) &&
|
|
222
|
-
isStringArray(value.activeStateIds) &&
|
|
223
|
-
isStringArray(value.tags) &&
|
|
224
|
-
(value.status === 'active' ||
|
|
225
|
-
value.status === 'done' ||
|
|
226
|
-
value.status === 'error' ||
|
|
227
|
-
value.status === 'stopped') &&
|
|
228
|
-
typeof value.quiescent === 'boolean' &&
|
|
229
|
-
(!Object.prototype.hasOwnProperty.call(value, 'stateId') ||
|
|
230
|
-
(isNonEmptyString(value.stateId) &&
|
|
231
|
-
value.activeStateIds.length === 1 &&
|
|
232
|
-
value.activeStateIds[0] === value.stateId)));
|
|
233
|
-
}
|
|
234
|
-
function nestedResultFromError(error) {
|
|
235
|
-
if (!(error instanceof Error))
|
|
236
|
-
return undefined;
|
|
237
|
-
const result = error.result;
|
|
238
|
-
return isRecord(result) ? result : undefined;
|
|
150
|
+
function childErrorOf(event) {
|
|
151
|
+
return isRecord(event) ? event.error : undefined;
|
|
239
152
|
}
|
|
240
|
-
function
|
|
241
|
-
if (!isRecord(value)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
isNonEmptyString(value.
|
|
246
|
-
value.kind === 'failure' &&
|
|
247
|
-
(!Object.prototype.hasOwnProperty.call(value, 'description') ||
|
|
248
|
-
typeof value.description === 'string'));
|
|
249
|
-
}
|
|
250
|
-
function normalizedChildFailure(error, playbookId) {
|
|
251
|
-
const result = nestedResultFromError(error);
|
|
252
|
-
if (result === undefined)
|
|
253
|
-
return undefined;
|
|
254
|
-
const allowed = new Set([
|
|
255
|
-
'status',
|
|
256
|
-
'playbookId',
|
|
257
|
-
'childSessionId',
|
|
258
|
-
'state',
|
|
259
|
-
...(result.status === 'ok' ? ['output', 'terminal'] : ['error']),
|
|
260
|
-
]);
|
|
261
|
-
if (Reflect.ownKeys(result).some((key) => typeof key !== 'string' || !allowed.has(key)) ||
|
|
262
|
-
(result.status !== 'aborted' &&
|
|
263
|
-
result.status !== 'error' &&
|
|
264
|
-
result.status !== 'ok') ||
|
|
265
|
-
result.playbookId !== playbookId) {
|
|
153
|
+
function branchSuccessOf(value) {
|
|
154
|
+
if (!isRecord(value) ||
|
|
155
|
+
value.status !== 'branched' ||
|
|
156
|
+
!isNonEmptyString(value.branch) ||
|
|
157
|
+
!isNonEmptyString(value.baseRevision) ||
|
|
158
|
+
!isNonEmptyString(value.issueSummary)) {
|
|
266
159
|
return undefined;
|
|
267
160
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
return {
|
|
281
|
-
status: 'ok',
|
|
282
|
-
...(result.output === undefined
|
|
283
|
-
? {}
|
|
284
|
-
: { output: result.output }),
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
if (Object.prototype.hasOwnProperty.call(result, 'state') &&
|
|
288
|
-
!isPlaybookState(result.state)) {
|
|
161
|
+
return {
|
|
162
|
+
branch: value.branch,
|
|
163
|
+
baseRevision: value.baseRevision,
|
|
164
|
+
issueSummary: value.issueSummary,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function decideSuccessOf(value) {
|
|
168
|
+
if (!isRecord(value) ||
|
|
169
|
+
value.noUnsettledFindings !== true ||
|
|
170
|
+
!isNonEmptyString(value.decideCommit) ||
|
|
171
|
+
!isNonEmptyString(value.evaluatedRevision)) {
|
|
289
172
|
return undefined;
|
|
290
173
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
(Object.prototype.hasOwnProperty.call(result.error, 'stack') &&
|
|
303
|
-
typeof result.error.stack !== 'string')) {
|
|
304
|
-
return undefined;
|
|
305
|
-
}
|
|
306
|
-
normalizedError = {
|
|
307
|
-
name: result.error.name,
|
|
308
|
-
message: result.error.message,
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
else if (result.status === 'error') {
|
|
174
|
+
return {
|
|
175
|
+
decideCommit: value.decideCommit,
|
|
176
|
+
evaluatedRevision: value.evaluatedRevision,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function codeSuccessOf(value) {
|
|
180
|
+
if (!isRecord(value) ||
|
|
181
|
+
value.status !== 'complete' ||
|
|
182
|
+
value.allReviewsPassed !== true ||
|
|
183
|
+
!isNonEmptyString(value.lastCodeCommit) ||
|
|
184
|
+
!isNonEmptyString(value.finalEvaluatedRevision)) {
|
|
312
185
|
return undefined;
|
|
313
186
|
}
|
|
314
187
|
return {
|
|
315
|
-
|
|
316
|
-
|
|
188
|
+
lastCodeCommit: value.lastCodeCommit,
|
|
189
|
+
finalEvaluatedRevision: value.finalEvaluatedRevision,
|
|
317
190
|
};
|
|
318
191
|
}
|
|
319
|
-
function
|
|
320
|
-
return ({ event }) =>
|
|
321
|
-
|
|
322
|
-
return normalizedChildFailure(error, playbookId) !== undefined;
|
|
323
|
-
};
|
|
192
|
+
function branchSucceededFor(path) {
|
|
193
|
+
return ({ context, event }) => context.pullRequestPath === path &&
|
|
194
|
+
branchSuccessOf(childOutputOf(event)) !== undefined;
|
|
324
195
|
}
|
|
325
|
-
function
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
196
|
+
function decideSucceeded({ event }) {
|
|
197
|
+
return decideSuccessOf(childOutputOf(event)) !== undefined;
|
|
198
|
+
}
|
|
199
|
+
function codeSucceededOnPullRequestPath({ context, event, }) {
|
|
200
|
+
return (context.pullRequestPath !== undefined &&
|
|
201
|
+
codeSuccessOf(childOutputOf(event)) !== undefined);
|
|
202
|
+
}
|
|
203
|
+
function codeSucceededOnPlainPath({ context, event, }) {
|
|
204
|
+
return (context.pullRequestPath === undefined &&
|
|
205
|
+
codeSuccessOf(childOutputOf(event)) !== undefined);
|
|
206
|
+
}
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Authored rejected child results.
|
|
209
|
+
export function authoredChildResult(error, expectedPlaybookId) {
|
|
210
|
+
if (!(error instanceof Error))
|
|
211
|
+
return undefined;
|
|
212
|
+
try {
|
|
213
|
+
const result = validatePlaybookCallResult(error.result, expectedPlaybookId);
|
|
214
|
+
return result.status !== 'ok' || result.terminal?.kind === 'failure'
|
|
215
|
+
? result
|
|
216
|
+
: undefined;
|
|
336
217
|
}
|
|
337
|
-
|
|
338
|
-
return
|
|
218
|
+
catch {
|
|
219
|
+
return undefined;
|
|
339
220
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (
|
|
221
|
+
}
|
|
222
|
+
function authoredResultFrom(playbookId) {
|
|
223
|
+
return ({ event }) => authoredChildResult(childErrorOf(event), playbookId) !== undefined;
|
|
224
|
+
}
|
|
225
|
+
function relayedChildResult(event, playbookId) {
|
|
226
|
+
const result = authoredChildResult(childErrorOf(event), playbookId);
|
|
227
|
+
if (result === undefined)
|
|
228
|
+
return undefined;
|
|
229
|
+
if (result.status === 'ok') {
|
|
230
|
+
// A child that completed at its own authored failure terminal relays its
|
|
231
|
+
// own output; an absent output is omitted rather than stored.
|
|
349
232
|
return {
|
|
350
233
|
playbookId,
|
|
351
234
|
status: 'ok',
|
|
352
|
-
...(
|
|
235
|
+
...(result.output === undefined ? {} : { output: result.output }),
|
|
353
236
|
};
|
|
354
237
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
238
|
+
const error = result.error === undefined
|
|
239
|
+
? {
|
|
240
|
+
name: 'AbortError',
|
|
241
|
+
message: `The ${playbookId} playbook call was aborted.`,
|
|
242
|
+
}
|
|
243
|
+
: { name: result.error.name, message: result.error.message };
|
|
244
|
+
return { playbookId, status: result.status, error };
|
|
360
245
|
}
|
|
361
246
|
function insufficientChildResult(event, playbookId) {
|
|
362
247
|
const output = childOutputOf(event);
|
|
363
248
|
return {
|
|
364
249
|
playbookId,
|
|
365
250
|
status: 'ok',
|
|
366
|
-
...(output === undefined ? {} : { output
|
|
251
|
+
...(output === undefined ? {} : { output }),
|
|
367
252
|
};
|
|
368
253
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
254
|
+
function normalizedControlError(error) {
|
|
255
|
+
if (error instanceof Error) {
|
|
256
|
+
return {
|
|
257
|
+
name: error.name.length > 0 ? error.name : 'Error',
|
|
258
|
+
message: error.message,
|
|
259
|
+
...(typeof error.stack === 'string' ? { stack: error.stack } : {}),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
name: 'Error',
|
|
264
|
+
message: typeof error === 'string' ? error : 'Actor failed with a non-Error value.',
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
// Discussion history and quoted-relay composition.
|
|
269
|
+
/** One consumed Analyst question and Boss reply, as relayed discussion. */
|
|
270
|
+
export function renderDiscussionExchange(question, answer) {
|
|
271
|
+
return `Analyst question: ${question}\nBoss reply: ${answer}`;
|
|
374
272
|
}
|
|
375
|
-
|
|
273
|
+
// Persists the answered pending Q&A pair into the typed discussion context
|
|
274
|
+
// before the pending fields are replaced or cleared, so later calls relay it.
|
|
275
|
+
function archivedDiscussion(context) {
|
|
376
276
|
if (context.pendingBossQuestion === undefined ||
|
|
377
277
|
!isNonEmptyString(context.bossReply)) {
|
|
378
|
-
return context.
|
|
278
|
+
return context.discussionContext;
|
|
379
279
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
{
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
.
|
|
394
|
-
.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return quotedRelay(planningRelayValues(context));
|
|
406
|
-
}
|
|
407
|
-
function decideCallText(context) {
|
|
408
|
-
return quotedRelay(planningRelayValues(context));
|
|
280
|
+
const exchange = renderDiscussionExchange(context.pendingBossQuestion.question, context.bossReply);
|
|
281
|
+
return context.discussionContext.length > 0
|
|
282
|
+
? `${context.discussionContext}\n\n${exchange}`
|
|
283
|
+
: exchange;
|
|
284
|
+
}
|
|
285
|
+
const RELAY_TOKEN = /<[a-z][a-z0-9-]*>/g;
|
|
286
|
+
// Quoted-relay composition: an empty value contributes no line, every
|
|
287
|
+
// continuation line of a multi-line value is quoted with `> ` (keeping LF/CRLF
|
|
288
|
+
// separators), and no quoted line is inserted. Substitution is single-pass and
|
|
289
|
+
// literal; a missing value keeps its placeholder literal.
|
|
290
|
+
function composeRelay(template, values) {
|
|
291
|
+
const lines = [];
|
|
292
|
+
for (const line of template) {
|
|
293
|
+
const tokens = line.match(RELAY_TOKEN) ?? [];
|
|
294
|
+
if (tokens.some((token) => values[token] === ''))
|
|
295
|
+
continue;
|
|
296
|
+
const composed = line.replace(RELAY_TOKEN, (token) => {
|
|
297
|
+
const value = values[token];
|
|
298
|
+
return value === undefined
|
|
299
|
+
? token
|
|
300
|
+
: value.replace(/\r?\n/g, (separator) => `${separator}> `);
|
|
301
|
+
});
|
|
302
|
+
lines.push(composed);
|
|
303
|
+
}
|
|
304
|
+
return lines.join('\n');
|
|
409
305
|
}
|
|
410
|
-
function
|
|
411
|
-
return
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
306
|
+
function plannedCallValues(context) {
|
|
307
|
+
return {
|
|
308
|
+
'<development-request>': context.developmentRequest,
|
|
309
|
+
'<discussion-context>': context.discussionContext,
|
|
310
|
+
'<planning-result>': context.planningResult,
|
|
311
|
+
};
|
|
416
312
|
}
|
|
417
|
-
function
|
|
418
|
-
return
|
|
313
|
+
function plannedCallText(context) {
|
|
314
|
+
return composeRelay(PLANNED_CALL_TEMPLATE, plannedCallValues(context));
|
|
315
|
+
}
|
|
316
|
+
function codeAfterDecideCallText(context) {
|
|
317
|
+
return composeRelay(CODE_AFTER_DECIDE_CALL_TEMPLATE, {
|
|
318
|
+
...plannedCallValues(context),
|
|
319
|
+
'<decide-commit>': context.decideCommit,
|
|
320
|
+
'<evaluated-revision>': context.evaluatedRevision,
|
|
321
|
+
});
|
|
419
322
|
}
|
|
420
|
-
// DEV-6: every placeholder is substituted from typed context that only a
|
|
421
|
-
// child's canonical structured result populated — never from player prose.
|
|
422
323
|
function pullRequestCallText(context) {
|
|
423
|
-
return
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
324
|
+
return composeRelay(PULL_REQUEST_CALL_TEMPLATE, {
|
|
325
|
+
'<development-request>': context.developmentRequest,
|
|
326
|
+
'<issue-summary>': context.issueSummary,
|
|
327
|
+
'<branch>': context.branch,
|
|
328
|
+
'<base-revision>': context.baseRevision,
|
|
329
|
+
'<last-code-commit>': context.lastCodeCommit,
|
|
330
|
+
'<final-evaluated-revision>': context.finalEvaluatedRevision,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// ---------------------------------------------------------------------------
|
|
334
|
+
// Boss entry and reply surfaces.
|
|
335
|
+
function startsDev({ event }) {
|
|
336
|
+
return (event.type === 'START_DEV' &&
|
|
337
|
+
typeof event.developmentRequest === 'string' &&
|
|
338
|
+
event.developmentRequest.trim().length > 0);
|
|
339
|
+
}
|
|
340
|
+
function emptyBossReply({ event }) {
|
|
341
|
+
return (event.type === 'BOSS_REPLY' &&
|
|
342
|
+
(typeof event.answer !== 'string' || event.answer.trim().length === 0));
|
|
343
|
+
}
|
|
344
|
+
function resumableStates(ids) {
|
|
345
|
+
return ids.map((id) => ({
|
|
346
|
+
guard: ({ context, event }) => event.type === 'BOSS_REPLY' &&
|
|
347
|
+
typeof event.answer === 'string' &&
|
|
348
|
+
event.answer.trim().length > 0 &&
|
|
349
|
+
context.pendingBossQuestion?.resumeStateId === id &&
|
|
350
|
+
(event.questionId === undefined || event.questionId === id),
|
|
351
|
+
target: `#${id}`,
|
|
352
|
+
reenter: true,
|
|
353
|
+
actions: 'rememberBossReply',
|
|
354
|
+
}));
|
|
441
355
|
}
|
|
442
356
|
const machineSetup = setup({
|
|
443
357
|
types: {},
|
|
@@ -450,188 +364,159 @@ const machineSetup = setup({
|
|
|
450
364
|
}),
|
|
451
365
|
},
|
|
452
366
|
guards: {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
context.pendingBossQuestion?.resumeStateId === 'planAnalysis' &&
|
|
472
|
-
(event.questionId === undefined || event.questionId === 'planAnalysis'),
|
|
367
|
+
startsDev,
|
|
368
|
+
emptyBossReply,
|
|
369
|
+
discussionCompleteAfterBossReply,
|
|
370
|
+
codePlanned: plannedPath('code'),
|
|
371
|
+
decideThenCodePlanned: plannedPath('decideThenCode'),
|
|
372
|
+
codeViaPullRequestPlanned: plannedPath('codeViaPullRequest'),
|
|
373
|
+
decideThenCodeViaPullRequestPlanned: plannedPath('decideThenCodeViaPullRequest'),
|
|
374
|
+
needsBossReplyWithQuestion,
|
|
375
|
+
needsBossReplyWithoutQuestion,
|
|
376
|
+
branchSucceededForCode: branchSucceededFor('code'),
|
|
377
|
+
branchSucceededForDecide: branchSucceededFor('decide-then-code'),
|
|
378
|
+
decideSucceeded,
|
|
379
|
+
codeSucceededOnPullRequestPath,
|
|
380
|
+
codeSucceededOnPlainPath,
|
|
381
|
+
authoredBranchResult: authoredResultFrom('branch'),
|
|
382
|
+
authoredDecideResult: authoredResultFrom('decide'),
|
|
383
|
+
authoredCodeResult: authoredResultFrom('code'),
|
|
384
|
+
authoredPrResult: authoredResultFrom('pr'),
|
|
473
385
|
},
|
|
474
386
|
actions: {
|
|
475
387
|
'playbook.acceptedOutcome': (_args, _params) => undefined,
|
|
476
|
-
startDev: assign(({
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
completion: undefined,
|
|
494
|
-
childOutput: undefined,
|
|
495
|
-
childFailure: undefined,
|
|
496
|
-
lastError: undefined,
|
|
497
|
-
pendingBossQuestion: undefined,
|
|
498
|
-
bossReply: undefined,
|
|
499
|
-
};
|
|
500
|
-
}),
|
|
388
|
+
startDev: assign(({ event }) => ({
|
|
389
|
+
developmentRequest: event.type === 'START_DEV' ? event.developmentRequest : undefined,
|
|
390
|
+
discussionContext: '',
|
|
391
|
+
planningResult: undefined,
|
|
392
|
+
pullRequestPath: undefined,
|
|
393
|
+
decideCommit: undefined,
|
|
394
|
+
evaluatedRevision: undefined,
|
|
395
|
+
branch: undefined,
|
|
396
|
+
baseRevision: undefined,
|
|
397
|
+
issueSummary: undefined,
|
|
398
|
+
lastCodeCommit: undefined,
|
|
399
|
+
finalEvaluatedRevision: undefined,
|
|
400
|
+
completion: undefined,
|
|
401
|
+
lastError: undefined,
|
|
402
|
+
pendingBossQuestion: undefined,
|
|
403
|
+
bossReply: undefined,
|
|
404
|
+
})),
|
|
501
405
|
completeDiscussion: assign(({ context }) => ({
|
|
502
|
-
|
|
503
|
-
completion: 'discussion-complete',
|
|
406
|
+
discussionContext: archivedDiscussion(context),
|
|
407
|
+
completion: { kind: 'discussion-complete' },
|
|
504
408
|
pendingBossQuestion: undefined,
|
|
505
409
|
bossReply: undefined,
|
|
506
410
|
})),
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
411
|
+
acceptPlainPlan: assign(({ context, event }) => ({
|
|
412
|
+
planningResult: outputText(event, 'planningResult'),
|
|
413
|
+
discussionContext: archivedDiscussion(context),
|
|
414
|
+
pendingBossQuestion: undefined,
|
|
415
|
+
bossReply: undefined,
|
|
416
|
+
})),
|
|
417
|
+
acceptPullRequestPlan: assign(({ context, event }, params) => ({
|
|
418
|
+
planningResult: outputText(event, 'planningResult'),
|
|
419
|
+
pullRequestPath: params.path,
|
|
420
|
+
discussionContext: archivedDiscussion(context),
|
|
421
|
+
pendingBossQuestion: undefined,
|
|
422
|
+
bossReply: undefined,
|
|
423
|
+
})),
|
|
424
|
+
setPendingBossQuestion: assign(({ context, event }, params) => ({
|
|
425
|
+
discussionContext: archivedDiscussion(context),
|
|
426
|
+
pendingBossQuestion: {
|
|
427
|
+
...params,
|
|
428
|
+
questionId: params.resumeStateId,
|
|
429
|
+
question: outputText(event, 'question') ?? '',
|
|
430
|
+
},
|
|
431
|
+
bossReply: undefined,
|
|
432
|
+
})),
|
|
528
433
|
rememberBossReply: assign(({ event }) => event.type === 'BOSS_REPLY' ? { bossReply: event.answer } : {}),
|
|
529
|
-
|
|
530
|
-
lastError: () => new Error('BOSS_REPLY received an empty answer'),
|
|
434
|
+
clearBossReplyContext: assign({
|
|
531
435
|
pendingBossQuestion: undefined,
|
|
532
436
|
bossReply: undefined,
|
|
533
437
|
}),
|
|
534
438
|
rememberBranchResult: assign(({ event }) => {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
return {};
|
|
538
|
-
return {
|
|
539
|
-
branch: output.branch,
|
|
540
|
-
baseRevision: output.baseRevision,
|
|
541
|
-
issueSummary: output.issueSummary,
|
|
542
|
-
};
|
|
439
|
+
const success = branchSuccessOf(childOutputOf(event));
|
|
440
|
+
return success === undefined ? {} : { ...success };
|
|
543
441
|
}),
|
|
544
442
|
rememberDecideResult: assign(({ event }) => {
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
return {};
|
|
548
|
-
return {
|
|
549
|
-
decideCommit: output.decideCommit,
|
|
550
|
-
evaluatedRevision: output.evaluatedRevision,
|
|
551
|
-
};
|
|
443
|
+
const success = decideSuccessOf(childOutputOf(event));
|
|
444
|
+
return success === undefined ? {} : { ...success };
|
|
552
445
|
}),
|
|
553
446
|
rememberCodeResult: assign(({ event }) => {
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
return {};
|
|
557
|
-
return {
|
|
558
|
-
lastCodeCommit: output.lastCodeCommit,
|
|
559
|
-
finalEvaluatedRevision: output.finalEvaluatedRevision,
|
|
560
|
-
};
|
|
447
|
+
const success = codeSuccessOf(childOutputOf(event));
|
|
448
|
+
return success === undefined ? {} : { ...success };
|
|
561
449
|
}),
|
|
562
|
-
completeWithChildSuccess: assign(({ event }) => {
|
|
450
|
+
completeWithChildSuccess: assign(({ event }, params) => {
|
|
563
451
|
const output = childOutputOf(event);
|
|
564
452
|
return {
|
|
565
|
-
completion:
|
|
566
|
-
|
|
453
|
+
completion: {
|
|
454
|
+
kind: 'complete',
|
|
455
|
+
childPlaybookId: params.childPlaybookId,
|
|
456
|
+
...(output === undefined ? {} : { childOutput: output }),
|
|
457
|
+
},
|
|
567
458
|
};
|
|
568
459
|
}),
|
|
569
|
-
|
|
570
|
-
completion:
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
completion: 'child-failed',
|
|
575
|
-
childFailure: insufficientChildResult(event, 'code'),
|
|
576
|
-
})),
|
|
577
|
-
completeWithInsufficientDecideResult: assign(({ event }) => ({
|
|
578
|
-
completion: 'child-failed',
|
|
579
|
-
childFailure: insufficientChildResult(event, 'decide'),
|
|
580
|
-
})),
|
|
581
|
-
completeWithBranchFailure: assign(({ event }) => ({
|
|
582
|
-
completion: 'child-failed',
|
|
583
|
-
childFailure: relayedChildFailure(event, 'branch'),
|
|
584
|
-
})),
|
|
585
|
-
completeWithCodeFailure: assign(({ event }) => ({
|
|
586
|
-
completion: 'child-failed',
|
|
587
|
-
childFailure: relayedChildFailure(event, 'code'),
|
|
588
|
-
})),
|
|
589
|
-
completeWithDecideFailure: assign(({ event }) => ({
|
|
590
|
-
completion: 'child-failed',
|
|
591
|
-
childFailure: relayedChildFailure(event, 'decide'),
|
|
592
|
-
})),
|
|
593
|
-
completeWithPrFailure: assign(({ event }) => ({
|
|
594
|
-
completion: 'child-failed',
|
|
595
|
-
childFailure: relayedChildFailure(event, 'pr'),
|
|
460
|
+
completeWithInsufficientResult: assign(({ event }, params) => ({
|
|
461
|
+
completion: {
|
|
462
|
+
kind: 'child-failed',
|
|
463
|
+
childResult: insufficientChildResult(event, params.playbookId),
|
|
464
|
+
},
|
|
596
465
|
})),
|
|
466
|
+
completeWithAuthoredChildResult: assign(({ event }, params) => {
|
|
467
|
+
const childResult = relayedChildResult(event, params.playbookId);
|
|
468
|
+
return childResult === undefined
|
|
469
|
+
? {}
|
|
470
|
+
: { completion: { kind: 'child-failed', childResult } };
|
|
471
|
+
}),
|
|
597
472
|
rememberActorError: assign(({ event }) => ({
|
|
598
|
-
lastError: event
|
|
473
|
+
lastError: normalizedControlError(childErrorOf(event)),
|
|
599
474
|
})),
|
|
600
475
|
rememberMalformedPlayerOutput: assign({
|
|
601
|
-
lastError:
|
|
476
|
+
lastError: {
|
|
477
|
+
name: 'MalformedActorOutput',
|
|
478
|
+
message: 'Analyst output for DEV-1 did not match an available declared result.',
|
|
479
|
+
},
|
|
480
|
+
pendingBossQuestion: undefined,
|
|
481
|
+
bossReply: undefined,
|
|
482
|
+
}),
|
|
483
|
+
rememberMalformedBossReply: assign({
|
|
484
|
+
lastError: {
|
|
485
|
+
name: 'MalformedBossReply',
|
|
486
|
+
message: 'BOSS_REPLY carried an empty or whitespace-only answer.',
|
|
487
|
+
},
|
|
488
|
+
pendingBossQuestion: undefined,
|
|
489
|
+
bossReply: undefined,
|
|
602
490
|
}),
|
|
603
491
|
},
|
|
604
492
|
});
|
|
605
493
|
export const devMachine = machineSetup.createMachine({
|
|
606
494
|
id: 'dev',
|
|
495
|
+
description: 'Plans the development path for a request and runs the child playbooks it selects.',
|
|
607
496
|
initial: 'ready',
|
|
608
497
|
context: ({ input }) => ({
|
|
609
|
-
runResults: typeof input
|
|
610
|
-
|
|
611
|
-
deliveryViaPullRequest: false,
|
|
498
|
+
runResults: typeof input?.runResults === 'string' ? input.runResults : '',
|
|
499
|
+
discussionContext: '',
|
|
612
500
|
}),
|
|
613
501
|
output: ({ context }) => {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if (context.completion === 'complete') {
|
|
618
|
-
return {
|
|
619
|
-
status: 'complete',
|
|
620
|
-
// `done` is entered by `pr` on a pull-request path and by `code` on a
|
|
621
|
-
// plain path; no other arm completes the workflow.
|
|
622
|
-
childPlaybookId: context.deliveryViaPullRequest ? 'pr' : 'code',
|
|
623
|
-
...(context.childOutput === undefined
|
|
624
|
-
? {}
|
|
625
|
-
: { childOutput: context.childOutput }),
|
|
626
|
-
};
|
|
502
|
+
const completion = context.completion;
|
|
503
|
+
if (completion === undefined) {
|
|
504
|
+
throw new Error('DEV reached a final state without a recorded completion');
|
|
627
505
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
506
|
+
switch (completion.kind) {
|
|
507
|
+
case 'discussion-complete':
|
|
508
|
+
return { status: 'discussion-complete' };
|
|
509
|
+
case 'complete':
|
|
510
|
+
return {
|
|
511
|
+
status: 'complete',
|
|
512
|
+
childPlaybookId: completion.childPlaybookId,
|
|
513
|
+
...(completion.childOutput === undefined
|
|
514
|
+
? {}
|
|
515
|
+
: { childOutput: completion.childOutput }),
|
|
516
|
+
};
|
|
517
|
+
case 'child-failed':
|
|
518
|
+
return { status: 'child-failed', childResult: completion.childResult };
|
|
633
519
|
}
|
|
634
|
-
throw new Error('DEV reached a final state without a recorded completion');
|
|
635
520
|
},
|
|
636
521
|
states: {
|
|
637
522
|
ready: {
|
|
@@ -640,13 +525,17 @@ export const devMachine = machineSetup.createMachine({
|
|
|
640
525
|
meta: playbookMeta('ready'),
|
|
641
526
|
tags: ['playbook.parked'],
|
|
642
527
|
on: {
|
|
643
|
-
START_DEV: {
|
|
528
|
+
START_DEV: {
|
|
529
|
+
guard: 'startsDev',
|
|
530
|
+
target: 'planAnalysis',
|
|
531
|
+
actions: 'startDev',
|
|
532
|
+
},
|
|
644
533
|
},
|
|
645
534
|
},
|
|
646
535
|
planAnalysis: {
|
|
647
536
|
id: 'planAnalysis',
|
|
648
537
|
description: STATE_DESCRIPTIONS.planAnalysis,
|
|
649
|
-
meta:
|
|
538
|
+
meta: roleMeta('planAnalysis', 'analyst'),
|
|
650
539
|
tags: ['playbook.busy'],
|
|
651
540
|
invoke: {
|
|
652
541
|
src: 'player',
|
|
@@ -655,9 +544,9 @@ export const devMachine = machineSetup.createMachine({
|
|
|
655
544
|
role: 'analyst',
|
|
656
545
|
sourceItem: 'DEV-1',
|
|
657
546
|
prompt: PLAN_ANALYSIS_PROMPT,
|
|
658
|
-
result:
|
|
547
|
+
result: PLAN_ANALYSIS_RESULT,
|
|
659
548
|
developmentRequest: context.developmentRequest ?? '',
|
|
660
|
-
discussionContext:
|
|
549
|
+
discussionContext: context.discussionContext,
|
|
661
550
|
runResults: context.runResults,
|
|
662
551
|
...(context.pendingBossQuestion === undefined
|
|
663
552
|
? {}
|
|
@@ -668,7 +557,27 @@ export const devMachine = machineSetup.createMachine({
|
|
|
668
557
|
}),
|
|
669
558
|
onDone: [
|
|
670
559
|
{
|
|
671
|
-
guard: '
|
|
560
|
+
guard: 'needsBossReplyWithQuestion',
|
|
561
|
+
target: 'awaitBossReply',
|
|
562
|
+
actions: [
|
|
563
|
+
{
|
|
564
|
+
type: 'playbook.acceptedOutcome',
|
|
565
|
+
params: {
|
|
566
|
+
source: 'planAnalysis',
|
|
567
|
+
target: 'awaitBossReply',
|
|
568
|
+
acceptedOutcome: 'needsBossReply',
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
{ type: 'setPendingBossQuestion', params: PLAN_ANALYSIS_QUESTION },
|
|
572
|
+
],
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
guard: 'needsBossReplyWithoutQuestion',
|
|
576
|
+
target: 'failed',
|
|
577
|
+
actions: 'rememberMalformedPlayerOutput',
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
guard: 'discussionCompleteAfterBossReply',
|
|
672
581
|
target: 'discussionComplete',
|
|
673
582
|
actions: [
|
|
674
583
|
{
|
|
@@ -683,7 +592,7 @@ export const devMachine = machineSetup.createMachine({
|
|
|
683
592
|
],
|
|
684
593
|
},
|
|
685
594
|
{
|
|
686
|
-
guard: '
|
|
595
|
+
guard: 'codePlanned',
|
|
687
596
|
target: 'callCode',
|
|
688
597
|
actions: [
|
|
689
598
|
{
|
|
@@ -694,11 +603,11 @@ export const devMachine = machineSetup.createMachine({
|
|
|
694
603
|
acceptedOutcome: 'code',
|
|
695
604
|
},
|
|
696
605
|
},
|
|
697
|
-
'
|
|
606
|
+
'acceptPlainPlan',
|
|
698
607
|
],
|
|
699
608
|
},
|
|
700
609
|
{
|
|
701
|
-
guard: '
|
|
610
|
+
guard: 'decideThenCodePlanned',
|
|
702
611
|
target: 'callDecide',
|
|
703
612
|
actions: [
|
|
704
613
|
{
|
|
@@ -709,11 +618,11 @@ export const devMachine = machineSetup.createMachine({
|
|
|
709
618
|
acceptedOutcome: 'decideThenCode',
|
|
710
619
|
},
|
|
711
620
|
},
|
|
712
|
-
'
|
|
621
|
+
'acceptPlainPlan',
|
|
713
622
|
],
|
|
714
623
|
},
|
|
715
624
|
{
|
|
716
|
-
guard: '
|
|
625
|
+
guard: 'codeViaPullRequestPlanned',
|
|
717
626
|
target: 'createBranch',
|
|
718
627
|
actions: [
|
|
719
628
|
{
|
|
@@ -724,11 +633,11 @@ export const devMachine = machineSetup.createMachine({
|
|
|
724
633
|
acceptedOutcome: 'codeViaPullRequest',
|
|
725
634
|
},
|
|
726
635
|
},
|
|
727
|
-
'
|
|
636
|
+
{ type: 'acceptPullRequestPlan', params: { path: 'code' } },
|
|
728
637
|
],
|
|
729
638
|
},
|
|
730
639
|
{
|
|
731
|
-
guard: '
|
|
640
|
+
guard: 'decideThenCodeViaPullRequestPlanned',
|
|
732
641
|
target: 'createBranch',
|
|
733
642
|
actions: [
|
|
734
643
|
{
|
|
@@ -739,22 +648,10 @@ export const devMachine = machineSetup.createMachine({
|
|
|
739
648
|
acceptedOutcome: 'decideThenCodeViaPullRequest',
|
|
740
649
|
},
|
|
741
650
|
},
|
|
742
|
-
'rememberDecideThenCodeViaPullRequestPath',
|
|
743
|
-
],
|
|
744
|
-
},
|
|
745
|
-
{
|
|
746
|
-
guard: 'needsBossReply',
|
|
747
|
-
target: 'awaitBossReply',
|
|
748
|
-
actions: [
|
|
749
651
|
{
|
|
750
|
-
type: '
|
|
751
|
-
params: {
|
|
752
|
-
source: 'planAnalysis',
|
|
753
|
-
target: 'awaitBossReply',
|
|
754
|
-
acceptedOutcome: 'needsBossReply',
|
|
755
|
-
},
|
|
652
|
+
type: 'acceptPullRequestPlan',
|
|
653
|
+
params: { path: 'decide-then-code' },
|
|
756
654
|
},
|
|
757
|
-
'rememberPendingQuestion',
|
|
758
655
|
],
|
|
759
656
|
},
|
|
760
657
|
{
|
|
@@ -762,7 +659,31 @@ export const devMachine = machineSetup.createMachine({
|
|
|
762
659
|
actions: 'rememberMalformedPlayerOutput',
|
|
763
660
|
},
|
|
764
661
|
],
|
|
765
|
-
onError: {
|
|
662
|
+
onError: {
|
|
663
|
+
target: 'failed',
|
|
664
|
+
actions: ['rememberActorError', 'clearBossReplyContext'],
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
awaitBossReply: {
|
|
669
|
+
id: 'awaitBossReply',
|
|
670
|
+
description: STATE_DESCRIPTIONS.awaitBossReply,
|
|
671
|
+
meta: playbookMeta('awaitBossReply'),
|
|
672
|
+
tags: ['playbook.parked'],
|
|
673
|
+
on: {
|
|
674
|
+
BOSS_REPLY: [
|
|
675
|
+
{
|
|
676
|
+
guard: 'emptyBossReply',
|
|
677
|
+
target: 'failed',
|
|
678
|
+
actions: 'rememberMalformedBossReply',
|
|
679
|
+
},
|
|
680
|
+
...resumableStates(['planAnalysis']),
|
|
681
|
+
],
|
|
682
|
+
START_DEV: {
|
|
683
|
+
guard: 'startsDev',
|
|
684
|
+
target: 'planAnalysis',
|
|
685
|
+
actions: 'startDev',
|
|
686
|
+
},
|
|
766
687
|
},
|
|
767
688
|
},
|
|
768
689
|
createBranch: {
|
|
@@ -776,29 +697,35 @@ export const devMachine = machineSetup.createMachine({
|
|
|
776
697
|
stateId: 'createBranch',
|
|
777
698
|
sourceItem: 'DEV-5',
|
|
778
699
|
playbookId: 'branch',
|
|
779
|
-
text:
|
|
700
|
+
text: plannedCallText(context),
|
|
780
701
|
}),
|
|
781
702
|
onDone: [
|
|
782
703
|
{
|
|
783
|
-
guard: '
|
|
704
|
+
guard: 'branchSucceededForCode',
|
|
784
705
|
target: 'callCode',
|
|
785
706
|
actions: 'rememberBranchResult',
|
|
786
707
|
},
|
|
787
708
|
{
|
|
788
|
-
guard: '
|
|
709
|
+
guard: 'branchSucceededForDecide',
|
|
789
710
|
target: 'callDecide',
|
|
790
711
|
actions: 'rememberBranchResult',
|
|
791
712
|
},
|
|
792
713
|
{
|
|
793
714
|
target: 'reportedChildFailure',
|
|
794
|
-
actions:
|
|
715
|
+
actions: {
|
|
716
|
+
type: 'completeWithInsufficientResult',
|
|
717
|
+
params: { playbookId: 'branch' },
|
|
718
|
+
},
|
|
795
719
|
},
|
|
796
720
|
],
|
|
797
721
|
onError: [
|
|
798
722
|
{
|
|
799
|
-
guard: '
|
|
723
|
+
guard: 'authoredBranchResult',
|
|
800
724
|
target: 'reportedChildFailure',
|
|
801
|
-
actions:
|
|
725
|
+
actions: {
|
|
726
|
+
type: 'completeWithAuthoredChildResult',
|
|
727
|
+
params: { playbookId: 'branch' },
|
|
728
|
+
},
|
|
802
729
|
},
|
|
803
730
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
804
731
|
],
|
|
@@ -815,29 +742,38 @@ export const devMachine = machineSetup.createMachine({
|
|
|
815
742
|
stateId: 'callCode',
|
|
816
743
|
sourceItem: 'DEV-2',
|
|
817
744
|
playbookId: 'code',
|
|
818
|
-
text:
|
|
745
|
+
text: plannedCallText(context),
|
|
819
746
|
}),
|
|
820
747
|
onDone: [
|
|
821
748
|
{
|
|
822
|
-
guard: '
|
|
749
|
+
guard: 'codeSucceededOnPullRequestPath',
|
|
823
750
|
target: 'openPullRequest',
|
|
824
751
|
actions: 'rememberCodeResult',
|
|
825
752
|
},
|
|
826
753
|
{
|
|
827
|
-
guard: '
|
|
754
|
+
guard: 'codeSucceededOnPlainPath',
|
|
828
755
|
target: 'done',
|
|
829
|
-
actions:
|
|
756
|
+
actions: {
|
|
757
|
+
type: 'completeWithChildSuccess',
|
|
758
|
+
params: { childPlaybookId: 'code' },
|
|
759
|
+
},
|
|
830
760
|
},
|
|
831
761
|
{
|
|
832
762
|
target: 'reportedChildFailure',
|
|
833
|
-
actions:
|
|
763
|
+
actions: {
|
|
764
|
+
type: 'completeWithInsufficientResult',
|
|
765
|
+
params: { playbookId: 'code' },
|
|
766
|
+
},
|
|
834
767
|
},
|
|
835
768
|
],
|
|
836
769
|
onError: [
|
|
837
770
|
{
|
|
838
|
-
guard: '
|
|
771
|
+
guard: 'authoredCodeResult',
|
|
839
772
|
target: 'reportedChildFailure',
|
|
840
|
-
actions:
|
|
773
|
+
actions: {
|
|
774
|
+
type: 'completeWithAuthoredChildResult',
|
|
775
|
+
params: { playbookId: 'code' },
|
|
776
|
+
},
|
|
841
777
|
},
|
|
842
778
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
843
779
|
],
|
|
@@ -854,24 +790,30 @@ export const devMachine = machineSetup.createMachine({
|
|
|
854
790
|
stateId: 'callDecide',
|
|
855
791
|
sourceItem: 'DEV-3',
|
|
856
792
|
playbookId: 'decide',
|
|
857
|
-
text:
|
|
793
|
+
text: plannedCallText(context),
|
|
858
794
|
}),
|
|
859
795
|
onDone: [
|
|
860
796
|
{
|
|
861
|
-
guard: '
|
|
797
|
+
guard: 'decideSucceeded',
|
|
862
798
|
target: 'callCodeAfterDecide',
|
|
863
799
|
actions: 'rememberDecideResult',
|
|
864
800
|
},
|
|
865
801
|
{
|
|
866
802
|
target: 'reportedChildFailure',
|
|
867
|
-
actions:
|
|
803
|
+
actions: {
|
|
804
|
+
type: 'completeWithInsufficientResult',
|
|
805
|
+
params: { playbookId: 'decide' },
|
|
806
|
+
},
|
|
868
807
|
},
|
|
869
808
|
],
|
|
870
809
|
onError: [
|
|
871
810
|
{
|
|
872
|
-
guard: '
|
|
811
|
+
guard: 'authoredDecideResult',
|
|
873
812
|
target: 'reportedChildFailure',
|
|
874
|
-
actions:
|
|
813
|
+
actions: {
|
|
814
|
+
type: 'completeWithAuthoredChildResult',
|
|
815
|
+
params: { playbookId: 'decide' },
|
|
816
|
+
},
|
|
875
817
|
},
|
|
876
818
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
877
819
|
],
|
|
@@ -892,25 +834,34 @@ export const devMachine = machineSetup.createMachine({
|
|
|
892
834
|
}),
|
|
893
835
|
onDone: [
|
|
894
836
|
{
|
|
895
|
-
guard: '
|
|
837
|
+
guard: 'codeSucceededOnPullRequestPath',
|
|
896
838
|
target: 'openPullRequest',
|
|
897
839
|
actions: 'rememberCodeResult',
|
|
898
840
|
},
|
|
899
841
|
{
|
|
900
|
-
guard: '
|
|
842
|
+
guard: 'codeSucceededOnPlainPath',
|
|
901
843
|
target: 'done',
|
|
902
|
-
actions:
|
|
844
|
+
actions: {
|
|
845
|
+
type: 'completeWithChildSuccess',
|
|
846
|
+
params: { childPlaybookId: 'code' },
|
|
847
|
+
},
|
|
903
848
|
},
|
|
904
849
|
{
|
|
905
850
|
target: 'reportedChildFailure',
|
|
906
|
-
actions:
|
|
851
|
+
actions: {
|
|
852
|
+
type: 'completeWithInsufficientResult',
|
|
853
|
+
params: { playbookId: 'code' },
|
|
854
|
+
},
|
|
907
855
|
},
|
|
908
856
|
],
|
|
909
857
|
onError: [
|
|
910
858
|
{
|
|
911
|
-
guard: '
|
|
859
|
+
guard: 'authoredCodeResult',
|
|
912
860
|
target: 'reportedChildFailure',
|
|
913
|
-
actions:
|
|
861
|
+
actions: {
|
|
862
|
+
type: 'completeWithAuthoredChildResult',
|
|
863
|
+
params: { playbookId: 'code' },
|
|
864
|
+
},
|
|
914
865
|
},
|
|
915
866
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
916
867
|
],
|
|
@@ -929,67 +880,58 @@ export const devMachine = machineSetup.createMachine({
|
|
|
929
880
|
playbookId: 'pr',
|
|
930
881
|
text: pullRequestCallText(context),
|
|
931
882
|
}),
|
|
932
|
-
//
|
|
933
|
-
//
|
|
934
|
-
onDone: {
|
|
883
|
+
// `pr` success completes DEV with that result; a `pr` failure
|
|
884
|
+
// terminal arrives through `onError` and is relayed below.
|
|
885
|
+
onDone: {
|
|
886
|
+
target: 'done',
|
|
887
|
+
actions: {
|
|
888
|
+
type: 'completeWithChildSuccess',
|
|
889
|
+
params: { childPlaybookId: 'pr' },
|
|
890
|
+
},
|
|
891
|
+
},
|
|
935
892
|
onError: [
|
|
936
893
|
{
|
|
937
|
-
guard: '
|
|
894
|
+
guard: 'authoredPrResult',
|
|
938
895
|
target: 'reportedChildFailure',
|
|
939
|
-
actions:
|
|
896
|
+
actions: {
|
|
897
|
+
type: 'completeWithAuthoredChildResult',
|
|
898
|
+
params: { playbookId: 'pr' },
|
|
899
|
+
},
|
|
940
900
|
},
|
|
941
901
|
{ target: 'failed', actions: 'rememberActorError' },
|
|
942
902
|
],
|
|
943
903
|
},
|
|
944
904
|
},
|
|
945
|
-
awaitBossReply: {
|
|
946
|
-
id: 'awaitBossReply',
|
|
947
|
-
description: STATE_DESCRIPTIONS.awaitBossReply,
|
|
948
|
-
meta: playbookMeta('awaitBossReply'),
|
|
949
|
-
tags: ['playbook.parked'],
|
|
950
|
-
on: {
|
|
951
|
-
BOSS_REPLY: [
|
|
952
|
-
{
|
|
953
|
-
guard: 'emptyBossReply',
|
|
954
|
-
target: '#failed',
|
|
955
|
-
actions: 'rememberEmptyBossReplyError',
|
|
956
|
-
},
|
|
957
|
-
{
|
|
958
|
-
guard: 'resumesPlanAnalysis',
|
|
959
|
-
target: '#planAnalysis',
|
|
960
|
-
reenter: true,
|
|
961
|
-
actions: 'rememberBossReply',
|
|
962
|
-
},
|
|
963
|
-
],
|
|
964
|
-
START_DEV: { target: 'planAnalysis', actions: 'startDev' },
|
|
965
|
-
},
|
|
966
|
-
},
|
|
967
905
|
failed: {
|
|
968
906
|
id: 'failed',
|
|
969
907
|
description: STATE_DESCRIPTIONS.failed,
|
|
970
908
|
meta: playbookMeta('failed'),
|
|
971
909
|
tags: ['playbook.parked'],
|
|
972
910
|
on: {
|
|
973
|
-
START_DEV: {
|
|
911
|
+
START_DEV: {
|
|
912
|
+
guard: 'startsDev',
|
|
913
|
+
target: 'planAnalysis',
|
|
914
|
+
actions: 'startDev',
|
|
915
|
+
},
|
|
974
916
|
},
|
|
975
917
|
},
|
|
976
918
|
discussionComplete: {
|
|
977
919
|
id: 'discussionComplete',
|
|
920
|
+
type: 'final',
|
|
978
921
|
description: STATE_DESCRIPTIONS.discussionComplete,
|
|
979
922
|
meta: terminalMeta('discussionComplete', 'success'),
|
|
980
|
-
type: 'final',
|
|
981
923
|
},
|
|
982
924
|
done: {
|
|
983
925
|
id: 'done',
|
|
926
|
+
type: 'final',
|
|
984
927
|
description: STATE_DESCRIPTIONS.done,
|
|
985
928
|
meta: terminalMeta('done', 'success'),
|
|
986
|
-
type: 'final',
|
|
987
929
|
},
|
|
988
930
|
reportedChildFailure: {
|
|
989
931
|
id: 'reportedChildFailure',
|
|
932
|
+
type: 'final',
|
|
990
933
|
description: STATE_DESCRIPTIONS.reportedChildFailure,
|
|
991
934
|
meta: terminalMeta('reportedChildFailure', 'failure'),
|
|
992
|
-
type: 'final',
|
|
993
935
|
},
|
|
994
936
|
},
|
|
995
937
|
});
|