@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,11 +1,16 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
//
|
|
4
|
-
// Generated by
|
|
4
|
+
// Generated by gears2fsm from ./pr.gears.md.
|
|
5
5
|
import { assign, fromPromise, setup } from 'xstate';
|
|
6
|
+
import { validatePlaybookCallResult } from '@sublang/playbook/xstate-runtime';
|
|
7
|
+
/** No parallel group: the playbook runs one delegated role at a time. */
|
|
8
|
+
export const concurrentRoleSets = [];
|
|
9
|
+
const CALLER_INPUT_PLACEHOLDER = '<caller-input>';
|
|
10
|
+
const PULL_REQUEST_URL_PLACEHOLDER = '<pull-request-url>';
|
|
11
|
+
/** The one nested target, shared by `invoke.input` and result validation. */
|
|
12
|
+
const CODE_PLAYBOOK_ID = 'code';
|
|
6
13
|
const OPEN_PULL_REQUEST_PROMPT = [
|
|
7
|
-
'> Original request: <caller-input>',
|
|
8
|
-
'',
|
|
9
14
|
'Publish the branch and open its pull request without changing any file or making any commit.',
|
|
10
15
|
'Confirm that the working tree is clean and that the checked-out branch is the branch to deliver, not the repository default branch.',
|
|
11
16
|
"Push the branch to the repository's GitHub remote with its upstream set; never force-push.",
|
|
@@ -13,102 +18,83 @@ const OPEN_PULL_REQUEST_PROMPT = [
|
|
|
13
18
|
'Give the pull request a title naming the change and a body with a summary of what changed and why from the base revision to the last commit, the verification the commits report, and a `Closes #N` line for issue number N when the request names one.',
|
|
14
19
|
'Report the pull request number and URL exactly.',
|
|
15
20
|
'If the working tree is not clean, the checked-out branch is wrong, the push is rejected, or the pull request cannot be opened, open nothing further and report the failure with its reason.',
|
|
21
|
+
'',
|
|
22
|
+
'> Original request: <caller-input>',
|
|
16
23
|
].join('\n');
|
|
17
|
-
const NEEDS_BOSS_REPLY_RESULT = "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>`.";
|
|
18
24
|
const OPEN_PULL_REQUEST_RESULTS = {
|
|
19
|
-
opened: '
|
|
20
|
-
notPublished: '
|
|
21
|
-
needsBossReply:
|
|
25
|
+
opened: "Coder's result affirmatively supports that the pull request is open. Output shall include `pullRequest: <exact pull request number as reported>` and `pullRequestUrl: <exact pull request URL as reported>`.",
|
|
26
|
+
notPublished: "Coder's result affirmatively supports that the branch or its pull request was not published; the workflow fails and returns Coder's complete result with its reason to its caller. Output shall include `coderOutput: <verbatim final text>`.",
|
|
27
|
+
needsBossReply: "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.",
|
|
22
28
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// A retained value is reported text, so it reaches the shell as one
|
|
32
|
-
// single-quoted literal word: no command substitution, no expansion, no word
|
|
33
|
-
// splitting. A single quote inside the value closes the literal, escapes
|
|
34
|
-
// itself, and reopens it — the only character single quotes cannot hold.
|
|
35
|
-
function shellLiteral(value) {
|
|
36
|
-
return `'${value.split("'").join("'\\''")}'`;
|
|
37
|
-
}
|
|
38
|
-
// Bind the retained identity into a script command. Without it the quoted
|
|
39
|
-
// placeholder stays, and no `gh` output equals it, so the step refuses instead
|
|
40
|
-
// of acting on whatever the checkout now infers. Every script state is
|
|
41
|
-
// reachable only after `rememberPullRequest`, so the refusal is unreachable by
|
|
42
|
-
// construction and exists to keep the failure closed if that ever changes.
|
|
43
|
-
function scriptCommand(command, context) {
|
|
44
|
-
const url = context.pullRequestUrl;
|
|
45
|
-
if (!isNonEmptyString(url))
|
|
46
|
-
return command;
|
|
47
|
-
// A function replacement keeps `$`-patterns in the value from being read as
|
|
48
|
-
// replacement syntax.
|
|
49
|
-
return command.replaceAll(QUOTED_PULL_REQUEST_URL, () => shellLiteral(url));
|
|
50
|
-
}
|
|
51
|
-
const WAIT_FOR_CHECKS_COMMAND = [
|
|
52
|
-
'n=0',
|
|
53
|
-
"while gh pr checks 2>&1 | grep -q 'no checks reported'; do",
|
|
54
|
-
'n=$((n + 1))',
|
|
55
|
-
'[ "$n" -ge 6 ] && exit 0',
|
|
56
|
-
'sleep 10',
|
|
57
|
-
'done',
|
|
58
|
-
'gh pr checks --watch --fail-fast >/dev/null 2>&1',
|
|
29
|
+
const CHECK_WAIT_COMMAND = [
|
|
30
|
+
`n=0`,
|
|
31
|
+
`while gh pr checks 2>&1 | grep -q 'no checks reported'; do`,
|
|
32
|
+
`n=$((n + 1))`,
|
|
33
|
+
`[ "$n" -ge 6 ] && exit 0`,
|
|
34
|
+
`sleep 10`,
|
|
35
|
+
`done`,
|
|
36
|
+
`gh pr checks --watch --fail-fast >/dev/null 2>&1`,
|
|
59
37
|
].join('\n');
|
|
60
|
-
const PUBLISH_FIX_COMMAND = [
|
|
61
|
-
'[ "$(gh pr view --json url --jq .url)" = \'<pull-request-url>\' ] || exit 1',
|
|
62
|
-
'git push || exit 1',
|
|
63
|
-
'n=0',
|
|
64
|
-
'until [ "$(gh pr view --json headRefOid --jq .headRefOid 2>/dev/null)" = "$(git rev-parse HEAD)" ]; do',
|
|
65
|
-
'n=$((n + 1))',
|
|
66
|
-
'[ "$n" -ge 12 ] && exit 1',
|
|
67
|
-
'sleep 5',
|
|
68
|
-
'done',
|
|
69
|
-
].join('\n');
|
|
70
|
-
const MERGE_PULL_REQUEST_COMMAND = "pr=$(gh pr view --json url --jq .url) || exit 1\n[ \"$pr\" = '<pull-request-url>' ] || exit 1\nbase=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name) || exit 1\n[ -n \"$base\" ] || exit 1\n[ \"$(gh pr view \"$pr\" --json baseRefName --jq .baseRefName)\" = \"$base\" ] || exit 1\ngh pr merge --merge --delete-branch --match-head-commit \"$(git rev-parse HEAD)\" || exit 1\n[ \"$(gh pr view \"$pr\" --json state --jq .state)\" = MERGED ] || exit 1\n[ \"$(git branch --show-current)\" = \"$base\" ]";
|
|
71
|
-
const UPDATE_LOCAL_DEFAULT_COMMAND = 'git pull --ff-only';
|
|
72
38
|
const WAIT_FOR_CHECKS_RESULTS = {
|
|
73
|
-
checksPassed:
|
|
74
|
-
checksFailed:
|
|
39
|
+
checksPassed: 'The command exited with status zero: the checks passed; a pull request whose repository still reports no checks after a brief wait for them to register counts as passed.',
|
|
40
|
+
checksFailed: 'The command exited with a nonzero status: the checks failed.',
|
|
75
41
|
};
|
|
42
|
+
const PUBLISH_FIX_COMMAND_TEMPLATE = [
|
|
43
|
+
`[ "$(gh pr view --json url --jq .url)" = '<pull-request-url>' ] || exit 1`,
|
|
44
|
+
`git push || exit 1`,
|
|
45
|
+
`n=0`,
|
|
46
|
+
`until [ "$(gh pr view --json headRefOid --jq .headRefOid 2>/dev/null)" = "$(git rev-parse HEAD)" ]; do`,
|
|
47
|
+
`n=$((n + 1))`,
|
|
48
|
+
`[ "$n" -ge 12 ] && exit 1`,
|
|
49
|
+
`sleep 5`,
|
|
50
|
+
`done`,
|
|
51
|
+
].join('\n');
|
|
76
52
|
const PUBLISH_FIX_RESULTS = {
|
|
77
|
-
fixPublished: "The command exited with status zero
|
|
78
|
-
fixNotPublished:
|
|
53
|
+
fixPublished: "The command exited with status zero, once the pull request's head is the pushed commit: the fix is published.",
|
|
54
|
+
fixNotPublished: 'The command exited with a nonzero status: the fix is not published, an authored failure of the workflow that leaves the pull request open.',
|
|
79
55
|
};
|
|
80
|
-
const
|
|
81
|
-
checksPassed:
|
|
82
|
-
checksStillFailing:
|
|
56
|
+
const WAIT_FOR_FIX_CHECKS_RESULTS = {
|
|
57
|
+
checksPassed: 'The command exited with status zero: the checks passed.',
|
|
58
|
+
checksStillFailing: 'The command exited with a nonzero status: the checks are still failing, an authored failure of the workflow that leaves the pull request open; there is no second fix attempt.',
|
|
83
59
|
};
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
60
|
+
const MERGE_COMMAND_TEMPLATE = [
|
|
61
|
+
`pr=$(gh pr view --json url --jq .url) || exit 1`,
|
|
62
|
+
`[ "$pr" = '<pull-request-url>' ] || exit 1`,
|
|
63
|
+
`base=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name) || exit 1`,
|
|
64
|
+
`[ -n "$base" ] || exit 1`,
|
|
65
|
+
`[ "$(gh pr view "$pr" --json baseRefName --jq .baseRefName)" = "$base" ] || exit 1`,
|
|
66
|
+
`gh pr merge --merge --delete-branch --match-head-commit "$(git rev-parse HEAD)" || exit 1`,
|
|
67
|
+
`[ "$(gh pr view "$pr" --json state --jq .state)" = MERGED ] || exit 1`,
|
|
68
|
+
`[ "$(git branch --show-current)" = "$base" ]`,
|
|
69
|
+
].join('\n');
|
|
70
|
+
const MERGE_RESULTS = {
|
|
71
|
+
merged: 'The command exited with status zero: the pull request is merged.',
|
|
72
|
+
mergeRefused: 'The command exited with a nonzero status: the merge is refused, an authored failure of the workflow that leaves the pull request in the state GitHub reports, because the checkout no longer infers the published pull request, the pull request targets another branch, GitHub refused the merge for a conflict, a branch protection, a forbidden merge method, or a head that moved, or the merge may have landed while its confirmation, the local switch to the default branch, or the branch deletion failed; a refused merge does not establish that the pull request is unmerged, so the workflow reports it as an unconfirmed merge rather than as not merged.',
|
|
87
73
|
};
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
const LOCAL_UPDATE_COMMAND = `git pull --ff-only`;
|
|
75
|
+
const LOCAL_UPDATE_RESULTS = {
|
|
76
|
+
localDefaultUpdated: 'The command exited with status zero: the local default branch is updated; the workflow completes, returning the pull request number and URL, the fact that the pull request is merged, and that the local default branch was fast-forwarded to the merged head.',
|
|
77
|
+
localDefaultNotUpdated: 'The command exited with a nonzero status: the local default branch is not updated; the workflow completes, since the pull request is merged either way, returning the pull request number and URL, the fact that the pull request is merged, and that the local default branch was not fast-forwarded to the merged head.',
|
|
91
78
|
};
|
|
92
|
-
// PR-3's coding request is static prompt text of the nested call template.
|
|
93
79
|
const FIX_CODING_REQUEST = "The pull request's checks are red on the checked-out branch. Inspect the failing checks with `gh pr checks` and `gh run view --log-failed`, fix their cause on this branch with a minimal change, and make the checks pass.";
|
|
94
80
|
const STATE_DESCRIPTIONS = {
|
|
95
|
-
ready: 'Waiting for
|
|
96
|
-
openPullRequest: 'Coder is publishing the branch and opening its pull request.',
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
81
|
+
ready: 'Waiting for a reviewed branch to deliver through a pull request.',
|
|
82
|
+
openPullRequest: 'Coder is publishing the branch and opening its pull request without changing any file or making any commit.',
|
|
83
|
+
awaitBossReply: "Waiting for Boss to answer the acting agent's question.",
|
|
84
|
+
waitForChecks: "Waiting for the pull request's checks before any fix attempt.",
|
|
85
|
+
fixChecks: "The CODE playbook is making the one fix attempt for the pull request's failing checks.",
|
|
86
|
+
publishFix: 'Pushing the fix to the published pull request and waiting until its head is the pushed commit.',
|
|
87
|
+
waitForFixChecks: "Waiting for the pull request's checks after the one fix attempt.",
|
|
88
|
+
mergePullRequest: 'Merging the published pull request into the repository default branch and confirming the merge.',
|
|
102
89
|
updateLocalDefault: 'Fast-forwarding the local default branch to the merged head.',
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
mergeRefused: 'The merge did not complete: the checked-out branch no longer carries the published pull request, the pull request does not target the repository default branch, GitHub refused the merge, or the merge may have landed while its confirmation, the local switch to the default branch, or the branch deletion failed; the pull request is in the state GitHub reports.',
|
|
90
|
+
failed: 'The pull-request workflow failed outside its authored outcomes and is waiting for a new delivery request.',
|
|
91
|
+
mergedLocalUpdated: 'The pull request is merged and the local default branch was fast-forwarded to the merged head.',
|
|
92
|
+
mergedLocalNotUpdated: 'The pull request is merged, but the local default branch was not fast-forwarded to the merged head.',
|
|
93
|
+
notPublished: "The branch or its pull request was not published; the workflow failed with Coder's complete result and its reason.",
|
|
94
|
+
fixFailed: "The one CODE fix attempt ended in an authored abort or failure, or a terminal result that did not prove its success; the workflow failed relaying that result and left the pull request open.",
|
|
95
|
+
fixNotPublished: 'The fix was not published; the workflow failed and left the pull request open.',
|
|
96
|
+
checksFailed: 'The checks are still failing after the one fix attempt; the workflow failed and left the pull request open.',
|
|
97
|
+
mergeUnconfirmed: 'The merge was refused or could not be confirmed; the workflow failed with an unconfirmed merge, leaving the pull request in the state GitHub reports.',
|
|
112
98
|
};
|
|
113
99
|
function playbookMeta(stateId, role) {
|
|
114
100
|
return {
|
|
@@ -119,9 +105,6 @@ function playbookMeta(stateId, role) {
|
|
|
119
105
|
},
|
|
120
106
|
};
|
|
121
107
|
}
|
|
122
|
-
// DR-048: a final state additionally declares whether its outcome means the
|
|
123
|
-
// workflow succeeded or failed, so PR's own caller learns that from the
|
|
124
|
-
// machine rather than from PR's output fields.
|
|
125
108
|
function terminalMeta(stateId, terminal) {
|
|
126
109
|
return {
|
|
127
110
|
playbook: {
|
|
@@ -141,277 +124,235 @@ function isRecord(value) {
|
|
|
141
124
|
function isNonEmptyString(value) {
|
|
142
125
|
return typeof value === 'string' && value.trim().length > 0;
|
|
143
126
|
}
|
|
127
|
+
function isCanonicalIndex(key, length) {
|
|
128
|
+
return /^(0|[1-9][0-9]*)$/.test(key) && Number(key) < length;
|
|
129
|
+
}
|
|
130
|
+
/** Strict JSON validation; tracks only the active recursion path. */
|
|
131
|
+
function isJsonValue(value, path = new Set()) {
|
|
132
|
+
if (value === null)
|
|
133
|
+
return true;
|
|
134
|
+
switch (typeof value) {
|
|
135
|
+
case 'boolean':
|
|
136
|
+
case 'string':
|
|
137
|
+
return true;
|
|
138
|
+
case 'number':
|
|
139
|
+
return Number.isFinite(value);
|
|
140
|
+
case 'object':
|
|
141
|
+
break;
|
|
142
|
+
default:
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
const container = value;
|
|
146
|
+
if (path.has(container))
|
|
147
|
+
return false;
|
|
148
|
+
path.add(container);
|
|
149
|
+
try {
|
|
150
|
+
if (Array.isArray(container)) {
|
|
151
|
+
if (Object.getPrototypeOf(container) !== Array.prototype)
|
|
152
|
+
return false;
|
|
153
|
+
const length = container.length;
|
|
154
|
+
const keys = Reflect.ownKeys(container);
|
|
155
|
+
if (keys.length !== length + 1)
|
|
156
|
+
return false;
|
|
157
|
+
for (const key of keys) {
|
|
158
|
+
if (typeof key !== 'string')
|
|
159
|
+
return false;
|
|
160
|
+
const descriptor = Object.getOwnPropertyDescriptor(container, key);
|
|
161
|
+
if (descriptor === undefined || !('value' in descriptor))
|
|
162
|
+
return false;
|
|
163
|
+
if (key === 'length') {
|
|
164
|
+
if (descriptor.enumerable || descriptor.configurable)
|
|
165
|
+
return false;
|
|
166
|
+
if (descriptor.value !== length)
|
|
167
|
+
return false;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (!isCanonicalIndex(key, length) || !descriptor.enumerable) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (!isJsonValue(descriptor.value, path))
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
if (!isRecord(container))
|
|
179
|
+
return false;
|
|
180
|
+
for (const key of Reflect.ownKeys(container)) {
|
|
181
|
+
if (typeof key !== 'string')
|
|
182
|
+
return false;
|
|
183
|
+
const descriptor = Object.getOwnPropertyDescriptor(container, key);
|
|
184
|
+
if (descriptor === undefined ||
|
|
185
|
+
!descriptor.enumerable ||
|
|
186
|
+
!('value' in descriptor)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (!isJsonValue(descriptor.value, path))
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
path.delete(container);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function normalizeError(error) {
|
|
199
|
+
if (error instanceof Error) {
|
|
200
|
+
return {
|
|
201
|
+
name: error.name || 'Error',
|
|
202
|
+
message: error.message,
|
|
203
|
+
...(typeof error.stack === 'string' ? { stack: error.stack } : {}),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return { name: 'Error', message: String(error) };
|
|
207
|
+
}
|
|
208
|
+
/** Binds a value as its own single-quoted shell literal. */
|
|
209
|
+
function shellLiteral(value) {
|
|
210
|
+
return `'${value.split("'").join("'\\''")}'`;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Replaces the single-quoted `'<pull-request-url>'` shell word, quotes
|
|
214
|
+
* included, with the bound literal; an absent value keeps the placeholder.
|
|
215
|
+
*/
|
|
216
|
+
function bindPullRequestUrl(template, context) {
|
|
217
|
+
const url = context.pullRequestUrl;
|
|
218
|
+
if (url === undefined)
|
|
219
|
+
return template;
|
|
220
|
+
return template
|
|
221
|
+
.split(`'${PULL_REQUEST_URL_PLACEHOLDER}'`)
|
|
222
|
+
.join(shellLiteral(url));
|
|
223
|
+
}
|
|
224
|
+
/** Quoted relay: empty values add no line; multi-line values quote each line. */
|
|
225
|
+
function quotedRelay(values) {
|
|
226
|
+
return values
|
|
227
|
+
.filter(([, value]) => value.length > 0)
|
|
228
|
+
.map(([label, value]) => `${label}: ${value}`
|
|
229
|
+
.split('\n')
|
|
230
|
+
.map((line) => `> ${line}`)
|
|
231
|
+
.join('\n'))
|
|
232
|
+
.join('\n');
|
|
233
|
+
}
|
|
234
|
+
function fixCallText(context) {
|
|
235
|
+
return quotedRelay([
|
|
236
|
+
['Original request', context.callerInput ?? CALLER_INPUT_PLACEHOLDER],
|
|
237
|
+
['Pull request', context.pullRequestUrl ?? PULL_REQUEST_URL_PLACEHOLDER],
|
|
238
|
+
['Coding request', FIX_CODING_REQUEST],
|
|
239
|
+
]);
|
|
240
|
+
}
|
|
144
241
|
function actorOutput(event) {
|
|
145
242
|
if (!isRecord(event))
|
|
146
243
|
return undefined;
|
|
147
244
|
return isRecord(event.output) ? event.output : undefined;
|
|
148
245
|
}
|
|
149
|
-
function outputGuard(event, guard) {
|
|
150
|
-
return actorOutput(event)?.guard === guard;
|
|
151
|
-
}
|
|
152
246
|
function outputString(event, field) {
|
|
153
247
|
const value = actorOutput(event)?.[field];
|
|
154
248
|
return isNonEmptyString(value) ? value : undefined;
|
|
155
249
|
}
|
|
250
|
+
function playerGuard(event, guard) {
|
|
251
|
+
return actorOutput(event)?.guard === guard;
|
|
252
|
+
}
|
|
156
253
|
function isOpened({ event }) {
|
|
157
|
-
return (
|
|
254
|
+
return (playerGuard(event, 'opened') &&
|
|
158
255
|
outputString(event, 'pullRequest') !== undefined &&
|
|
159
256
|
outputString(event, 'pullRequestUrl') !== undefined);
|
|
160
257
|
}
|
|
161
258
|
function isNotPublished({ event }) {
|
|
162
|
-
return (
|
|
259
|
+
return (playerGuard(event, 'notPublished') &&
|
|
163
260
|
outputString(event, 'coderOutput') !== undefined);
|
|
164
261
|
}
|
|
165
262
|
function needsBossReply({ event }) {
|
|
166
|
-
return (
|
|
263
|
+
return (playerGuard(event, 'needsBossReply') &&
|
|
167
264
|
outputString(event, 'question') !== undefined);
|
|
168
265
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
function scriptExitStatus(event) {
|
|
172
|
-
const value = actorOutput(event)?.exitStatus;
|
|
173
|
-
return Number.isInteger(value) ? value : undefined;
|
|
174
|
-
}
|
|
175
|
-
function zeroExitGuard(guard) {
|
|
176
|
-
return ({ event }) => outputGuard(event, guard) && scriptExitStatus(event) === 0;
|
|
177
|
-
}
|
|
178
|
-
function nonzeroExitGuard(guard) {
|
|
266
|
+
/** Script outcome: the declared guard, consistent with its exit status. */
|
|
267
|
+
function scriptOutcome(guard, exit) {
|
|
179
268
|
return ({ event }) => {
|
|
180
|
-
const
|
|
181
|
-
|
|
269
|
+
const output = actorOutput(event);
|
|
270
|
+
if (output === undefined || output.guard !== guard)
|
|
271
|
+
return false;
|
|
272
|
+
const status = output.exitStatus;
|
|
273
|
+
if (typeof status !== 'number' || !Number.isInteger(status))
|
|
274
|
+
return false;
|
|
275
|
+
return exit === 'zero' ? status === 0 : status !== 0;
|
|
182
276
|
};
|
|
183
277
|
}
|
|
184
278
|
function childOutputOf(event) {
|
|
185
279
|
return isRecord(event) ? event.output : undefined;
|
|
186
280
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return (isRecord(value) &&
|
|
192
|
-
value.status === 'complete' &&
|
|
193
|
-
isNonEmptyString(value.lastCodeCommit) &&
|
|
194
|
-
value.allReviewsPassed === true);
|
|
195
|
-
}
|
|
281
|
+
/**
|
|
282
|
+
* The public success variant of the `code` output interface: CODE completed
|
|
283
|
+
* with every review passed, naming its last commit and evaluated revision.
|
|
284
|
+
*/
|
|
196
285
|
function isCodeSuccess({ event }) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return false;
|
|
204
|
-
const next = new Set(ancestors).add(value);
|
|
205
|
-
return Reflect.ownKeys(value).every((key) => typeof key === 'string' &&
|
|
206
|
-
Object.prototype.propertyIsEnumerable.call(value, key) &&
|
|
207
|
-
isStateValue(value[key], next));
|
|
208
|
-
}
|
|
209
|
-
function isStringArray(value) {
|
|
210
|
-
return (Array.isArray(value) &&
|
|
211
|
-
Reflect.ownKeys(value).length === value.length + 1 &&
|
|
212
|
-
Reflect.ownKeys(value).every((key) => {
|
|
213
|
-
if (key === 'length')
|
|
214
|
-
return true;
|
|
215
|
-
return (typeof key === 'string' &&
|
|
216
|
-
/^(0|[1-9][0-9]*)$/.test(key) &&
|
|
217
|
-
Number(key) < value.length);
|
|
218
|
-
}) &&
|
|
219
|
-
value.every((entry) => isNonEmptyString(entry)) &&
|
|
220
|
-
new Set(value).size === value.length);
|
|
286
|
+
const output = childOutputOf(event);
|
|
287
|
+
return (isRecord(output) &&
|
|
288
|
+
output.status === 'complete' &&
|
|
289
|
+
isNonEmptyString(output.lastCodeCommit) &&
|
|
290
|
+
isNonEmptyString(output.finalEvaluatedRevision) &&
|
|
291
|
+
output.allReviewsPassed === true);
|
|
221
292
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
'value',
|
|
227
|
-
'activeStateIds',
|
|
228
|
-
'tags',
|
|
229
|
-
'status',
|
|
230
|
-
'quiescent',
|
|
231
|
-
'stateId',
|
|
232
|
-
]);
|
|
233
|
-
if (Reflect.ownKeys(value).some((key) => typeof key !== 'string' || !allowed.has(key)) ||
|
|
234
|
-
!Object.prototype.hasOwnProperty.call(value, 'value') ||
|
|
235
|
-
!Object.prototype.hasOwnProperty.call(value, 'activeStateIds') ||
|
|
236
|
-
!Object.prototype.hasOwnProperty.call(value, 'tags') ||
|
|
237
|
-
!Object.prototype.hasOwnProperty.call(value, 'status') ||
|
|
238
|
-
!Object.prototype.hasOwnProperty.call(value, 'quiescent')) {
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
return (isStateValue(value.value) &&
|
|
242
|
-
isStringArray(value.activeStateIds) &&
|
|
243
|
-
isStringArray(value.tags) &&
|
|
244
|
-
(value.status === 'active' ||
|
|
245
|
-
value.status === 'done' ||
|
|
246
|
-
value.status === 'error' ||
|
|
247
|
-
value.status === 'stopped') &&
|
|
248
|
-
typeof value.quiescent === 'boolean' &&
|
|
249
|
-
(!Object.prototype.hasOwnProperty.call(value, 'stateId') ||
|
|
250
|
-
(isNonEmptyString(value.stateId) &&
|
|
251
|
-
value.activeStateIds.length === 1 &&
|
|
252
|
-
value.activeStateIds[0] === value.stateId)));
|
|
293
|
+
/** A delivered CODE result that does not prove success, as valid JSON. */
|
|
294
|
+
function isInsufficientCodeResult({ event }) {
|
|
295
|
+
const output = childOutputOf(event);
|
|
296
|
+
return output === undefined || isJsonValue(output);
|
|
253
297
|
}
|
|
254
|
-
function
|
|
298
|
+
export function authoredChildResult(error, expectedPlaybookId) {
|
|
255
299
|
if (!(error instanceof Error))
|
|
256
300
|
return undefined;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return false;
|
|
263
|
-
const allowed = new Set(['stateId', 'kind', 'description']);
|
|
264
|
-
return (Reflect.ownKeys(value).every((key) => typeof key === 'string' && allowed.has(key)) &&
|
|
265
|
-
isNonEmptyString(value.stateId) &&
|
|
266
|
-
value.kind === 'failure' &&
|
|
267
|
-
(!Object.prototype.hasOwnProperty.call(value, 'description') ||
|
|
268
|
-
typeof value.description === 'string'));
|
|
269
|
-
}
|
|
270
|
-
function normalizedChildFailure(error, playbookId) {
|
|
271
|
-
const result = nestedResultFromError(error);
|
|
272
|
-
if (result === undefined)
|
|
273
|
-
return undefined;
|
|
274
|
-
const allowed = new Set([
|
|
275
|
-
'status',
|
|
276
|
-
'playbookId',
|
|
277
|
-
'childSessionId',
|
|
278
|
-
'state',
|
|
279
|
-
...(result.status === 'ok' ? ['output', 'terminal'] : ['error']),
|
|
280
|
-
]);
|
|
281
|
-
if (Reflect.ownKeys(result).some((key) => typeof key !== 'string' || !allowed.has(key)) ||
|
|
282
|
-
(result.status !== 'aborted' &&
|
|
283
|
-
result.status !== 'error' &&
|
|
284
|
-
result.status !== 'ok') ||
|
|
285
|
-
result.playbookId !== playbookId) {
|
|
286
|
-
return undefined;
|
|
287
|
-
}
|
|
288
|
-
if (Object.prototype.hasOwnProperty.call(result, 'childSessionId') &&
|
|
289
|
-
!isNonEmptyString(result.childSessionId)) {
|
|
290
|
-
return undefined;
|
|
291
|
-
}
|
|
292
|
-
if (result.status === 'ok') {
|
|
293
|
-
if (!Object.prototype.hasOwnProperty.call(result, 'terminal') ||
|
|
294
|
-
!isFailureTerminalRecord(result.terminal) ||
|
|
295
|
-
!isNonEmptyString(result.childSessionId) ||
|
|
296
|
-
(Object.prototype.hasOwnProperty.call(result, 'state') &&
|
|
297
|
-
!isPlaybookState(result.state))) {
|
|
298
|
-
return undefined;
|
|
299
|
-
}
|
|
300
|
-
return {
|
|
301
|
-
status: 'ok',
|
|
302
|
-
...(result.output === undefined
|
|
303
|
-
? {}
|
|
304
|
-
: { output: result.output }),
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
if (Object.prototype.hasOwnProperty.call(result, 'state') &&
|
|
308
|
-
!isPlaybookState(result.state)) {
|
|
309
|
-
return undefined;
|
|
310
|
-
}
|
|
311
|
-
let normalizedError;
|
|
312
|
-
if (Object.prototype.hasOwnProperty.call(result, 'error')) {
|
|
313
|
-
if (!isRecord(result.error))
|
|
314
|
-
return undefined;
|
|
315
|
-
const errorKeys = Reflect.ownKeys(result.error);
|
|
316
|
-
if (errorKeys.some((key) => typeof key !== 'string' ||
|
|
317
|
-
(key !== 'name' && key !== 'message' && key !== 'stack')) ||
|
|
318
|
-
!Object.prototype.hasOwnProperty.call(result.error, 'name') ||
|
|
319
|
-
!Object.prototype.hasOwnProperty.call(result.error, 'message') ||
|
|
320
|
-
!isNonEmptyString(result.error.name) ||
|
|
321
|
-
typeof result.error.message !== 'string' ||
|
|
322
|
-
(Object.prototype.hasOwnProperty.call(result.error, 'stack') &&
|
|
323
|
-
typeof result.error.stack !== 'string')) {
|
|
324
|
-
return undefined;
|
|
325
|
-
}
|
|
326
|
-
normalizedError = {
|
|
327
|
-
name: result.error.name,
|
|
328
|
-
message: result.error.message,
|
|
329
|
-
};
|
|
301
|
+
try {
|
|
302
|
+
const result = validatePlaybookCallResult(error.result, expectedPlaybookId);
|
|
303
|
+
return result.status !== 'ok' || result.terminal?.kind === 'failure'
|
|
304
|
+
? result
|
|
305
|
+
: undefined;
|
|
330
306
|
}
|
|
331
|
-
|
|
307
|
+
catch {
|
|
332
308
|
return undefined;
|
|
333
309
|
}
|
|
334
|
-
return {
|
|
335
|
-
status: result.status,
|
|
336
|
-
...(normalizedError === undefined ? {} : { error: normalizedError }),
|
|
337
|
-
};
|
|
338
310
|
}
|
|
339
|
-
function
|
|
340
|
-
return (
|
|
341
|
-
const error = isRecord(event) ? event.error : undefined;
|
|
342
|
-
return normalizedChildFailure(error, playbookId) !== undefined;
|
|
343
|
-
};
|
|
311
|
+
function eventError(event) {
|
|
312
|
+
return isRecord(event) ? event.error : undefined;
|
|
344
313
|
}
|
|
345
|
-
function
|
|
346
|
-
|
|
347
|
-
const failure = normalizedChildFailure(outer, playbookId);
|
|
348
|
-
if (failure !== undefined && failure.status !== 'ok' && failure.error) {
|
|
349
|
-
return failure.error;
|
|
350
|
-
}
|
|
351
|
-
if (failure?.status === 'aborted') {
|
|
352
|
-
return {
|
|
353
|
-
name: 'AbortError',
|
|
354
|
-
message: `${playbookId.toUpperCase()} was aborted.`,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
if (outer instanceof Error) {
|
|
358
|
-
return { name: outer.name || 'Error', message: outer.message };
|
|
359
|
-
}
|
|
360
|
-
return { name: 'Error', message: String(outer) };
|
|
314
|
+
function isAuthoredCodeFailure({ event }) {
|
|
315
|
+
return authoredChildResult(eventError(event), CODE_PLAYBOOK_ID) !== undefined;
|
|
361
316
|
}
|
|
362
|
-
function
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
// success does through the retained onDone field check.
|
|
368
|
-
if (failure?.status === 'ok') {
|
|
317
|
+
function relayedCodeResult(event) {
|
|
318
|
+
const result = authoredChildResult(eventError(event), CODE_PLAYBOOK_ID);
|
|
319
|
+
if (result === undefined)
|
|
320
|
+
return undefined;
|
|
321
|
+
if (result.status === 'ok') {
|
|
369
322
|
return {
|
|
370
|
-
playbookId,
|
|
323
|
+
playbookId: CODE_PLAYBOOK_ID,
|
|
371
324
|
status: 'ok',
|
|
372
|
-
...(
|
|
325
|
+
...(result.output === undefined ? {} : { output: result.output }),
|
|
373
326
|
};
|
|
374
327
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
328
|
+
const error = result.error === undefined
|
|
329
|
+
? {
|
|
330
|
+
name: 'AbortError',
|
|
331
|
+
message: `${CODE_PLAYBOOK_ID.toUpperCase()} was aborted.`,
|
|
332
|
+
}
|
|
333
|
+
: { name: result.error.name, message: result.error.message };
|
|
334
|
+
return { playbookId: CODE_PLAYBOOK_ID, status: result.status, error };
|
|
380
335
|
}
|
|
381
|
-
function
|
|
336
|
+
function insufficientCodeResult(event) {
|
|
382
337
|
const output = childOutputOf(event);
|
|
383
338
|
return {
|
|
384
|
-
playbookId,
|
|
339
|
+
playbookId: CODE_PLAYBOOK_ID,
|
|
385
340
|
status: 'ok',
|
|
386
|
-
...(output === undefined
|
|
341
|
+
...(output === undefined || !isJsonValue(output)
|
|
342
|
+
? {}
|
|
343
|
+
: { output: output }),
|
|
387
344
|
};
|
|
388
345
|
}
|
|
389
|
-
function
|
|
390
|
-
return
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
export function fixChecksCallText(context) {
|
|
400
|
-
return quotedRelay([
|
|
401
|
-
['Original request', context.callerInput ?? ''],
|
|
402
|
-
['Pull request', context.pullRequestUrl ?? ''],
|
|
403
|
-
['Coding request', FIX_CODING_REQUEST],
|
|
404
|
-
]);
|
|
405
|
-
}
|
|
406
|
-
function pullRequestIdentity(context) {
|
|
407
|
-
if (!isNonEmptyString(context.pullRequest) ||
|
|
408
|
-
!isNonEmptyString(context.pullRequestUrl)) {
|
|
409
|
-
throw new Error('PR reached a final state without its pull request identity');
|
|
410
|
-
}
|
|
411
|
-
return {
|
|
412
|
-
pullRequest: context.pullRequest,
|
|
413
|
-
pullRequestUrl: context.pullRequestUrl,
|
|
414
|
-
};
|
|
346
|
+
function resumableStates(ids) {
|
|
347
|
+
return ids.map((id) => ({
|
|
348
|
+
guard: ({ context, event }) => event.type === 'BOSS_REPLY' &&
|
|
349
|
+
event.answer.trim().length > 0 &&
|
|
350
|
+
context.pendingBossQuestion?.resumeStateId === id &&
|
|
351
|
+
(event.questionId === undefined || event.questionId === id),
|
|
352
|
+
target: `#${id}`,
|
|
353
|
+
reenter: true,
|
|
354
|
+
actions: 'rememberBossReply',
|
|
355
|
+
}));
|
|
415
356
|
}
|
|
416
357
|
const machineSetup = setup({
|
|
417
358
|
types: {},
|
|
@@ -427,26 +368,24 @@ const machineSetup = setup({
|
|
|
427
368
|
}),
|
|
428
369
|
},
|
|
429
370
|
guards: {
|
|
371
|
+
validStartPr: ({ event }) => event.type === 'START_PR' && isNonEmptyString(event.callerInput),
|
|
430
372
|
isOpened,
|
|
431
373
|
isNotPublished,
|
|
432
374
|
needsBossReply,
|
|
433
|
-
checksPassed: zeroExitGuard('checksPassed'),
|
|
434
|
-
checksFailed: nonzeroExitGuard('checksFailed'),
|
|
435
|
-
isCodeSuccess,
|
|
436
|
-
authoredCodeFailure: authoredChildFailureGuard('code'),
|
|
437
|
-
fixPublished: zeroExitGuard('fixPublished'),
|
|
438
|
-
fixNotPublished: nonzeroExitGuard('fixNotPublished'),
|
|
439
|
-
checksStillFailing: nonzeroExitGuard('checksStillFailing'),
|
|
440
|
-
merged: zeroExitGuard('merged'),
|
|
441
|
-
mergeRefused: nonzeroExitGuard('mergeRefused'),
|
|
442
|
-
localDefaultUpdated: zeroExitGuard('localDefaultUpdated'),
|
|
443
|
-
localDefaultNotUpdated: nonzeroExitGuard('localDefaultNotUpdated'),
|
|
444
375
|
emptyBossReply: ({ event }) => event.type === 'BOSS_REPLY' && event.answer.trim().length === 0,
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
376
|
+
waitForChecksPassed: scriptOutcome('checksPassed', 'zero'),
|
|
377
|
+
waitForChecksFailed: scriptOutcome('checksFailed', 'nonzero'),
|
|
378
|
+
isCodeSuccess,
|
|
379
|
+
isInsufficientCodeResult,
|
|
380
|
+
isAuthoredCodeFailure,
|
|
381
|
+
fixPublished: scriptOutcome('fixPublished', 'zero'),
|
|
382
|
+
fixNotPublished: scriptOutcome('fixNotPublished', 'nonzero'),
|
|
383
|
+
waitForFixChecksPassed: scriptOutcome('checksPassed', 'zero'),
|
|
384
|
+
checksStillFailing: scriptOutcome('checksStillFailing', 'nonzero'),
|
|
385
|
+
merged: scriptOutcome('merged', 'zero'),
|
|
386
|
+
mergeRefused: scriptOutcome('mergeRefused', 'nonzero'),
|
|
387
|
+
localDefaultUpdated: scriptOutcome('localDefaultUpdated', 'zero'),
|
|
388
|
+
localDefaultNotUpdated: scriptOutcome('localDefaultNotUpdated', 'nonzero'),
|
|
450
389
|
},
|
|
451
390
|
actions: {
|
|
452
391
|
'playbook.acceptedOutcome': (_args, _params) => undefined,
|
|
@@ -457,133 +396,159 @@ const machineSetup = setup({
|
|
|
457
396
|
callerInput: event.callerInput,
|
|
458
397
|
pullRequest: undefined,
|
|
459
398
|
pullRequestUrl: undefined,
|
|
460
|
-
completion: undefined,
|
|
461
|
-
localDefaultUpdated: undefined,
|
|
462
399
|
coderOutput: undefined,
|
|
463
|
-
|
|
400
|
+
childResult: undefined,
|
|
401
|
+
localDefaultUpdated: undefined,
|
|
402
|
+
completion: undefined,
|
|
464
403
|
lastError: undefined,
|
|
465
404
|
pendingBossQuestion: undefined,
|
|
466
405
|
bossReply: undefined,
|
|
467
406
|
};
|
|
468
407
|
}),
|
|
469
|
-
|
|
408
|
+
rememberOpened: assign(({ event }) => ({
|
|
470
409
|
pullRequest: outputString(event, 'pullRequest'),
|
|
471
410
|
pullRequestUrl: outputString(event, 'pullRequestUrl'),
|
|
472
|
-
pendingBossQuestion: undefined,
|
|
473
|
-
bossReply: undefined,
|
|
474
411
|
})),
|
|
475
412
|
completeNotPublished: assign(({ event }) => ({
|
|
476
|
-
completion: 'not-published',
|
|
477
413
|
coderOutput: outputString(event, 'coderOutput'),
|
|
478
|
-
|
|
479
|
-
bossReply: undefined,
|
|
414
|
+
completion: 'not-published',
|
|
480
415
|
})),
|
|
481
|
-
|
|
416
|
+
setPendingBossQuestion: assign(({ event }) => {
|
|
482
417
|
const question = outputString(event, 'question');
|
|
483
|
-
if (question === undefined
|
|
484
|
-
return {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
asker: { kind: 'role', roleId: 'coder' },
|
|
492
|
-
question,
|
|
493
|
-
},
|
|
494
|
-
bossReply: undefined,
|
|
418
|
+
if (question === undefined)
|
|
419
|
+
return {};
|
|
420
|
+
const pending = {
|
|
421
|
+
questionId: 'openPullRequest',
|
|
422
|
+
resumeStateId: 'openPullRequest',
|
|
423
|
+
sourceItem: 'PR-1',
|
|
424
|
+
asker: { kind: 'role', roleId: 'coder' },
|
|
425
|
+
question,
|
|
495
426
|
};
|
|
427
|
+
return { pendingBossQuestion: pending, bossReply: undefined };
|
|
428
|
+
}),
|
|
429
|
+
clearBossReplyContext: assign({
|
|
430
|
+
pendingBossQuestion: undefined,
|
|
431
|
+
bossReply: undefined,
|
|
496
432
|
}),
|
|
497
433
|
rememberBossReply: assign(({ event }) => event.type === 'BOSS_REPLY' ? { bossReply: event.answer } : {}),
|
|
498
434
|
rememberEmptyBossReplyError: assign({
|
|
499
|
-
lastError: () =>
|
|
435
|
+
lastError: () => ({
|
|
436
|
+
name: 'Error',
|
|
437
|
+
message: 'BOSS_REPLY received an empty answer',
|
|
438
|
+
}),
|
|
500
439
|
pendingBossQuestion: undefined,
|
|
501
440
|
bossReply: undefined,
|
|
502
441
|
}),
|
|
442
|
+
completeWithCodeFailure: assign(({ event }) => {
|
|
443
|
+
const childResult = relayedCodeResult(event);
|
|
444
|
+
return childResult === undefined
|
|
445
|
+
? {}
|
|
446
|
+
: { childResult, completion: 'fix-failed' };
|
|
447
|
+
}),
|
|
503
448
|
completeWithInsufficientCodeResult: assign(({ event }) => ({
|
|
449
|
+
childResult: insufficientCodeResult(event),
|
|
504
450
|
completion: 'fix-failed',
|
|
505
|
-
childFailure: insufficientChildResult(event, 'code'),
|
|
506
|
-
})),
|
|
507
|
-
completeWithCodeFailure: assign(({ event }) => ({
|
|
508
|
-
completion: 'fix-failed',
|
|
509
|
-
childFailure: relayedChildFailure(event, 'code'),
|
|
510
451
|
})),
|
|
511
452
|
completeFixNotPublished: assign({
|
|
512
453
|
completion: 'fix-not-published',
|
|
513
454
|
}),
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}),
|
|
517
|
-
completeMergeRefused: assign({
|
|
455
|
+
completeChecksFailed: assign({ completion: 'checks-failed' }),
|
|
456
|
+
completeMergeUnconfirmed: assign({
|
|
518
457
|
completion: 'merge-unconfirmed',
|
|
519
458
|
}),
|
|
520
|
-
|
|
459
|
+
completeMergedLocalUpdated: assign({
|
|
521
460
|
completion: 'merged',
|
|
522
461
|
localDefaultUpdated: true,
|
|
523
462
|
}),
|
|
524
|
-
|
|
463
|
+
completeMergedLocalNotUpdated: assign({
|
|
525
464
|
completion: 'merged',
|
|
526
465
|
localDefaultUpdated: false,
|
|
527
466
|
}),
|
|
528
467
|
rememberActorError: assign(({ event }) => ({
|
|
529
|
-
lastError: event
|
|
468
|
+
lastError: normalizeError(eventError(event)),
|
|
530
469
|
})),
|
|
531
470
|
rememberMalformedPlayerOutput: assign({
|
|
532
|
-
lastError: () =>
|
|
471
|
+
lastError: () => ({
|
|
472
|
+
name: 'Error',
|
|
473
|
+
message: 'Coder result for PR-1 did not match an available outcome.',
|
|
474
|
+
}),
|
|
475
|
+
}),
|
|
476
|
+
rememberMalformedScriptOutput: assign({
|
|
477
|
+
lastError: () => ({
|
|
478
|
+
name: 'Error',
|
|
479
|
+
message: 'Script result did not match its declared outcomes.',
|
|
480
|
+
}),
|
|
481
|
+
}),
|
|
482
|
+
rememberMalformedChildOutput: assign({
|
|
483
|
+
lastError: () => ({
|
|
484
|
+
name: 'TypeError',
|
|
485
|
+
message: 'CODE delivered output that is not JSON-safe.',
|
|
486
|
+
}),
|
|
533
487
|
}),
|
|
534
|
-
rememberMalformedScriptOutput: assign((_args, params) => ({
|
|
535
|
-
lastError: new Error(`Script result for ${params.sourceItem} did not match a declared outcome.`),
|
|
536
|
-
})),
|
|
537
488
|
},
|
|
538
489
|
});
|
|
490
|
+
function pullRequestIdentity(context) {
|
|
491
|
+
if (!isNonEmptyString(context.pullRequest) ||
|
|
492
|
+
!isNonEmptyString(context.pullRequestUrl)) {
|
|
493
|
+
throw new Error('PR reached a final state without the reported pull request number and URL');
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
pullRequest: context.pullRequest,
|
|
497
|
+
pullRequestUrl: context.pullRequestUrl,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
539
500
|
export const prMachine = machineSetup.createMachine({
|
|
540
501
|
id: 'pr',
|
|
541
502
|
initial: 'ready',
|
|
542
|
-
context:
|
|
503
|
+
context: {},
|
|
543
504
|
output: ({ context }) => {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
505
|
+
switch (context.completion) {
|
|
506
|
+
case 'merged': {
|
|
507
|
+
if (typeof context.localDefaultUpdated !== 'boolean') {
|
|
508
|
+
throw new Error('PR reached merged without the local update fact');
|
|
509
|
+
}
|
|
510
|
+
return {
|
|
511
|
+
status: 'merged',
|
|
512
|
+
...pullRequestIdentity(context),
|
|
513
|
+
localDefaultUpdated: context.localDefaultUpdated,
|
|
514
|
+
};
|
|
554
515
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
516
|
+
case 'not-published': {
|
|
517
|
+
if (!isNonEmptyString(context.coderOutput)) {
|
|
518
|
+
throw new Error("PR reached not-published without Coder's report");
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
status: 'not-merged',
|
|
522
|
+
reason: 'not-published',
|
|
523
|
+
coderOutput: context.coderOutput,
|
|
524
|
+
};
|
|
564
525
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
526
|
+
case 'fix-failed': {
|
|
527
|
+
if (context.childResult === undefined) {
|
|
528
|
+
throw new Error('PR reached fix-failed without the CODE result');
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
status: 'not-merged',
|
|
532
|
+
reason: 'fix-failed',
|
|
533
|
+
...pullRequestIdentity(context),
|
|
534
|
+
childResult: context.childResult,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
case 'fix-not-published':
|
|
538
|
+
case 'checks-failed':
|
|
539
|
+
return {
|
|
540
|
+
status: 'not-merged',
|
|
541
|
+
reason: context.completion,
|
|
542
|
+
...pullRequestIdentity(context),
|
|
543
|
+
};
|
|
544
|
+
case 'merge-unconfirmed':
|
|
545
|
+
return {
|
|
546
|
+
status: 'merge-unconfirmed',
|
|
547
|
+
...pullRequestIdentity(context),
|
|
548
|
+
};
|
|
549
|
+
default:
|
|
550
|
+
throw new Error('PR reached a final state without a recorded completion');
|
|
585
551
|
}
|
|
586
|
-
throw new Error('PR reached a final state without a recorded completion');
|
|
587
552
|
},
|
|
588
553
|
states: {
|
|
589
554
|
ready: {
|
|
@@ -592,7 +557,11 @@ export const prMachine = machineSetup.createMachine({
|
|
|
592
557
|
meta: playbookMeta('ready'),
|
|
593
558
|
tags: ['playbook.parked'],
|
|
594
559
|
on: {
|
|
595
|
-
START_PR: {
|
|
560
|
+
START_PR: {
|
|
561
|
+
guard: 'validStartPr',
|
|
562
|
+
target: 'openPullRequest',
|
|
563
|
+
actions: 'startPr',
|
|
564
|
+
},
|
|
596
565
|
},
|
|
597
566
|
},
|
|
598
567
|
openPullRequest: {
|
|
@@ -608,7 +577,7 @@ export const prMachine = machineSetup.createMachine({
|
|
|
608
577
|
sourceItem: 'PR-1',
|
|
609
578
|
prompt: OPEN_PULL_REQUEST_PROMPT,
|
|
610
579
|
result: OPEN_PULL_REQUEST_RESULTS,
|
|
611
|
-
callerInput: context.callerInput ??
|
|
580
|
+
callerInput: context.callerInput ?? CALLER_INPUT_PLACEHOLDER,
|
|
612
581
|
...(context.pendingBossQuestion === undefined
|
|
613
582
|
? {}
|
|
614
583
|
: { pendingBossQuestion: context.pendingBossQuestion }),
|
|
@@ -629,7 +598,8 @@ export const prMachine = machineSetup.createMachine({
|
|
|
629
598
|
acceptedOutcome: 'opened',
|
|
630
599
|
},
|
|
631
600
|
},
|
|
632
|
-
'
|
|
601
|
+
'rememberOpened',
|
|
602
|
+
'clearBossReplyContext',
|
|
633
603
|
],
|
|
634
604
|
},
|
|
635
605
|
{
|
|
@@ -645,6 +615,7 @@ export const prMachine = machineSetup.createMachine({
|
|
|
645
615
|
},
|
|
646
616
|
},
|
|
647
617
|
'completeNotPublished',
|
|
618
|
+
'clearBossReplyContext',
|
|
648
619
|
],
|
|
649
620
|
},
|
|
650
621
|
{
|
|
@@ -659,15 +630,33 @@ export const prMachine = machineSetup.createMachine({
|
|
|
659
630
|
acceptedOutcome: 'needsBossReply',
|
|
660
631
|
},
|
|
661
632
|
},
|
|
662
|
-
'
|
|
633
|
+
'setPendingBossQuestion',
|
|
663
634
|
],
|
|
664
635
|
},
|
|
636
|
+
{ target: 'failed', actions: 'rememberMalformedPlayerOutput' },
|
|
637
|
+
],
|
|
638
|
+
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
awaitBossReply: {
|
|
642
|
+
id: 'awaitBossReply',
|
|
643
|
+
description: STATE_DESCRIPTIONS.awaitBossReply,
|
|
644
|
+
meta: playbookMeta('awaitBossReply'),
|
|
645
|
+
tags: ['playbook.parked'],
|
|
646
|
+
on: {
|
|
647
|
+
BOSS_REPLY: [
|
|
665
648
|
{
|
|
666
|
-
|
|
667
|
-
|
|
649
|
+
guard: 'emptyBossReply',
|
|
650
|
+
target: '#failed',
|
|
651
|
+
actions: 'rememberEmptyBossReplyError',
|
|
668
652
|
},
|
|
653
|
+
...resumableStates(['openPullRequest']),
|
|
669
654
|
],
|
|
670
|
-
|
|
655
|
+
START_PR: {
|
|
656
|
+
guard: 'validStartPr',
|
|
657
|
+
target: 'openPullRequest',
|
|
658
|
+
actions: 'startPr',
|
|
659
|
+
},
|
|
671
660
|
},
|
|
672
661
|
},
|
|
673
662
|
waitForChecks: {
|
|
@@ -680,19 +669,13 @@ export const prMachine = machineSetup.createMachine({
|
|
|
680
669
|
input: () => ({
|
|
681
670
|
stateId: 'waitForChecks',
|
|
682
671
|
sourceItem: 'PR-2',
|
|
683
|
-
command:
|
|
672
|
+
command: CHECK_WAIT_COMMAND,
|
|
684
673
|
result: WAIT_FOR_CHECKS_RESULTS,
|
|
685
674
|
}),
|
|
686
675
|
onDone: [
|
|
687
|
-
{ guard: '
|
|
688
|
-
{ guard: '
|
|
689
|
-
{
|
|
690
|
-
target: 'failed',
|
|
691
|
-
actions: {
|
|
692
|
-
type: 'rememberMalformedScriptOutput',
|
|
693
|
-
params: { sourceItem: 'PR-2' },
|
|
694
|
-
},
|
|
695
|
-
},
|
|
676
|
+
{ guard: 'waitForChecksPassed', target: 'mergePullRequest' },
|
|
677
|
+
{ guard: 'waitForChecksFailed', target: 'fixChecks' },
|
|
678
|
+
{ target: 'failed', actions: 'rememberMalformedScriptOutput' },
|
|
696
679
|
],
|
|
697
680
|
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
698
681
|
},
|
|
@@ -707,19 +690,21 @@ export const prMachine = machineSetup.createMachine({
|
|
|
707
690
|
input: ({ context }) => ({
|
|
708
691
|
stateId: 'fixChecks',
|
|
709
692
|
sourceItem: 'PR-3',
|
|
710
|
-
playbookId:
|
|
711
|
-
text:
|
|
693
|
+
playbookId: CODE_PLAYBOOK_ID,
|
|
694
|
+
text: fixCallText(context),
|
|
712
695
|
}),
|
|
713
696
|
onDone: [
|
|
714
697
|
{ guard: 'isCodeSuccess', target: 'publishFix' },
|
|
715
698
|
{
|
|
699
|
+
guard: 'isInsufficientCodeResult',
|
|
716
700
|
target: 'fixFailed',
|
|
717
701
|
actions: 'completeWithInsufficientCodeResult',
|
|
718
702
|
},
|
|
703
|
+
{ target: 'failed', actions: 'rememberMalformedChildOutput' },
|
|
719
704
|
],
|
|
720
705
|
onError: [
|
|
721
706
|
{
|
|
722
|
-
guard: '
|
|
707
|
+
guard: 'isAuthoredCodeFailure',
|
|
723
708
|
target: 'fixFailed',
|
|
724
709
|
actions: 'completeWithCodeFailure',
|
|
725
710
|
},
|
|
@@ -737,54 +722,42 @@ export const prMachine = machineSetup.createMachine({
|
|
|
737
722
|
input: ({ context }) => ({
|
|
738
723
|
stateId: 'publishFix',
|
|
739
724
|
sourceItem: 'PR-4',
|
|
740
|
-
command:
|
|
725
|
+
command: bindPullRequestUrl(PUBLISH_FIX_COMMAND_TEMPLATE, context),
|
|
741
726
|
result: PUBLISH_FIX_RESULTS,
|
|
742
727
|
}),
|
|
743
728
|
onDone: [
|
|
744
|
-
{ guard: 'fixPublished', target: '
|
|
729
|
+
{ guard: 'fixPublished', target: 'waitForFixChecks' },
|
|
745
730
|
{
|
|
746
731
|
guard: 'fixNotPublished',
|
|
747
732
|
target: 'fixNotPublished',
|
|
748
733
|
actions: 'completeFixNotPublished',
|
|
749
734
|
},
|
|
750
|
-
{
|
|
751
|
-
target: 'failed',
|
|
752
|
-
actions: {
|
|
753
|
-
type: 'rememberMalformedScriptOutput',
|
|
754
|
-
params: { sourceItem: 'PR-4' },
|
|
755
|
-
},
|
|
756
|
-
},
|
|
735
|
+
{ target: 'failed', actions: 'rememberMalformedScriptOutput' },
|
|
757
736
|
],
|
|
758
737
|
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
759
738
|
},
|
|
760
739
|
},
|
|
761
|
-
|
|
762
|
-
id: '
|
|
763
|
-
description: STATE_DESCRIPTIONS.
|
|
764
|
-
meta: playbookMeta('
|
|
740
|
+
waitForFixChecks: {
|
|
741
|
+
id: 'waitForFixChecks',
|
|
742
|
+
description: STATE_DESCRIPTIONS.waitForFixChecks,
|
|
743
|
+
meta: playbookMeta('waitForFixChecks'),
|
|
765
744
|
tags: ['playbook.busy'],
|
|
766
745
|
invoke: {
|
|
767
746
|
src: 'script',
|
|
768
747
|
input: () => ({
|
|
769
|
-
stateId: '
|
|
748
|
+
stateId: 'waitForFixChecks',
|
|
770
749
|
sourceItem: 'PR-5',
|
|
771
|
-
command:
|
|
772
|
-
result:
|
|
750
|
+
command: CHECK_WAIT_COMMAND,
|
|
751
|
+
result: WAIT_FOR_FIX_CHECKS_RESULTS,
|
|
773
752
|
}),
|
|
774
753
|
onDone: [
|
|
775
|
-
{ guard: '
|
|
754
|
+
{ guard: 'waitForFixChecksPassed', target: 'mergePullRequest' },
|
|
776
755
|
{
|
|
777
756
|
guard: 'checksStillFailing',
|
|
778
|
-
target: '
|
|
779
|
-
actions: '
|
|
780
|
-
},
|
|
781
|
-
{
|
|
782
|
-
target: 'failed',
|
|
783
|
-
actions: {
|
|
784
|
-
type: 'rememberMalformedScriptOutput',
|
|
785
|
-
params: { sourceItem: 'PR-5' },
|
|
786
|
-
},
|
|
757
|
+
target: 'checksFailed',
|
|
758
|
+
actions: 'completeChecksFailed',
|
|
787
759
|
},
|
|
760
|
+
{ target: 'failed', actions: 'rememberMalformedScriptOutput' },
|
|
788
761
|
],
|
|
789
762
|
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
790
763
|
},
|
|
@@ -799,23 +772,17 @@ export const prMachine = machineSetup.createMachine({
|
|
|
799
772
|
input: ({ context }) => ({
|
|
800
773
|
stateId: 'mergePullRequest',
|
|
801
774
|
sourceItem: 'PR-6',
|
|
802
|
-
command:
|
|
803
|
-
result:
|
|
775
|
+
command: bindPullRequestUrl(MERGE_COMMAND_TEMPLATE, context),
|
|
776
|
+
result: MERGE_RESULTS,
|
|
804
777
|
}),
|
|
805
778
|
onDone: [
|
|
806
779
|
{ guard: 'merged', target: 'updateLocalDefault' },
|
|
807
780
|
{
|
|
808
781
|
guard: 'mergeRefused',
|
|
809
|
-
target: '
|
|
810
|
-
actions: '
|
|
811
|
-
},
|
|
812
|
-
{
|
|
813
|
-
target: 'failed',
|
|
814
|
-
actions: {
|
|
815
|
-
type: 'rememberMalformedScriptOutput',
|
|
816
|
-
params: { sourceItem: 'PR-6' },
|
|
817
|
-
},
|
|
782
|
+
target: 'mergeUnconfirmed',
|
|
783
|
+
actions: 'completeMergeUnconfirmed',
|
|
818
784
|
},
|
|
785
|
+
{ target: 'failed', actions: 'rememberMalformedScriptOutput' },
|
|
819
786
|
],
|
|
820
787
|
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
821
788
|
},
|
|
@@ -830,72 +797,48 @@ export const prMachine = machineSetup.createMachine({
|
|
|
830
797
|
input: () => ({
|
|
831
798
|
stateId: 'updateLocalDefault',
|
|
832
799
|
sourceItem: 'PR-7',
|
|
833
|
-
command:
|
|
834
|
-
result:
|
|
800
|
+
command: LOCAL_UPDATE_COMMAND,
|
|
801
|
+
result: LOCAL_UPDATE_RESULTS,
|
|
835
802
|
}),
|
|
836
803
|
onDone: [
|
|
837
804
|
{
|
|
838
805
|
guard: 'localDefaultUpdated',
|
|
839
|
-
target: '
|
|
840
|
-
actions: '
|
|
806
|
+
target: 'mergedLocalUpdated',
|
|
807
|
+
actions: 'completeMergedLocalUpdated',
|
|
841
808
|
},
|
|
842
809
|
{
|
|
843
810
|
guard: 'localDefaultNotUpdated',
|
|
844
|
-
target: '
|
|
845
|
-
actions: '
|
|
846
|
-
},
|
|
847
|
-
{
|
|
848
|
-
target: 'failed',
|
|
849
|
-
actions: {
|
|
850
|
-
type: 'rememberMalformedScriptOutput',
|
|
851
|
-
params: { sourceItem: 'PR-7' },
|
|
852
|
-
},
|
|
811
|
+
target: 'mergedLocalNotUpdated',
|
|
812
|
+
actions: 'completeMergedLocalNotUpdated',
|
|
853
813
|
},
|
|
814
|
+
{ target: 'failed', actions: 'rememberMalformedScriptOutput' },
|
|
854
815
|
],
|
|
855
816
|
onError: { target: 'failed', actions: 'rememberActorError' },
|
|
856
817
|
},
|
|
857
818
|
},
|
|
858
|
-
awaitBossReply: {
|
|
859
|
-
id: 'awaitBossReply',
|
|
860
|
-
description: STATE_DESCRIPTIONS.awaitBossReply,
|
|
861
|
-
meta: playbookMeta('awaitBossReply'),
|
|
862
|
-
tags: ['playbook.parked'],
|
|
863
|
-
on: {
|
|
864
|
-
BOSS_REPLY: [
|
|
865
|
-
{
|
|
866
|
-
guard: 'emptyBossReply',
|
|
867
|
-
target: '#failed',
|
|
868
|
-
actions: 'rememberEmptyBossReplyError',
|
|
869
|
-
},
|
|
870
|
-
{
|
|
871
|
-
guard: 'resumesOpenPullRequest',
|
|
872
|
-
target: '#openPullRequest',
|
|
873
|
-
reenter: true,
|
|
874
|
-
actions: 'rememberBossReply',
|
|
875
|
-
},
|
|
876
|
-
],
|
|
877
|
-
START_PR: { target: 'openPullRequest', actions: 'startPr' },
|
|
878
|
-
},
|
|
879
|
-
},
|
|
880
819
|
failed: {
|
|
881
820
|
id: 'failed',
|
|
882
821
|
description: STATE_DESCRIPTIONS.failed,
|
|
883
822
|
meta: playbookMeta('failed'),
|
|
884
823
|
tags: ['playbook.parked'],
|
|
885
824
|
on: {
|
|
886
|
-
START_PR: {
|
|
825
|
+
START_PR: {
|
|
826
|
+
guard: 'validStartPr',
|
|
827
|
+
target: 'openPullRequest',
|
|
828
|
+
actions: 'startPr',
|
|
829
|
+
},
|
|
887
830
|
},
|
|
888
831
|
},
|
|
889
|
-
|
|
890
|
-
id: '
|
|
891
|
-
description: STATE_DESCRIPTIONS.
|
|
892
|
-
meta: terminalMeta('
|
|
832
|
+
mergedLocalUpdated: {
|
|
833
|
+
id: 'mergedLocalUpdated',
|
|
834
|
+
description: STATE_DESCRIPTIONS.mergedLocalUpdated,
|
|
835
|
+
meta: terminalMeta('mergedLocalUpdated', 'success'),
|
|
893
836
|
type: 'final',
|
|
894
837
|
},
|
|
895
|
-
|
|
896
|
-
id: '
|
|
897
|
-
description: STATE_DESCRIPTIONS.
|
|
898
|
-
meta: terminalMeta('
|
|
838
|
+
mergedLocalNotUpdated: {
|
|
839
|
+
id: 'mergedLocalNotUpdated',
|
|
840
|
+
description: STATE_DESCRIPTIONS.mergedLocalNotUpdated,
|
|
841
|
+
meta: terminalMeta('mergedLocalNotUpdated', 'success'),
|
|
899
842
|
type: 'final',
|
|
900
843
|
},
|
|
901
844
|
notPublished: {
|
|
@@ -916,16 +859,16 @@ export const prMachine = machineSetup.createMachine({
|
|
|
916
859
|
meta: terminalMeta('fixNotPublished', 'failure'),
|
|
917
860
|
type: 'final',
|
|
918
861
|
},
|
|
919
|
-
|
|
920
|
-
id: '
|
|
921
|
-
description: STATE_DESCRIPTIONS.
|
|
922
|
-
meta: terminalMeta('
|
|
862
|
+
checksFailed: {
|
|
863
|
+
id: 'checksFailed',
|
|
864
|
+
description: STATE_DESCRIPTIONS.checksFailed,
|
|
865
|
+
meta: terminalMeta('checksFailed', 'failure'),
|
|
923
866
|
type: 'final',
|
|
924
867
|
},
|
|
925
|
-
|
|
926
|
-
id: '
|
|
927
|
-
description: STATE_DESCRIPTIONS.
|
|
928
|
-
meta: terminalMeta('
|
|
868
|
+
mergeUnconfirmed: {
|
|
869
|
+
id: 'mergeUnconfirmed',
|
|
870
|
+
description: STATE_DESCRIPTIONS.mergeUnconfirmed,
|
|
871
|
+
meta: terminalMeta('mergeUnconfirmed', 'failure'),
|
|
929
872
|
type: 'final',
|
|
930
873
|
},
|
|
931
874
|
},
|