@sublang/playbook 13.0.0 → 13.2.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 +7 -5
- package/docs/cli.md +28 -9
- package/docs/configuration.md +21 -5
- package/docs/embedding.md +1 -1
- package/package.json +41 -3
- package/reference/sdlc/branch.md +37 -0
- package/reference/sdlc/branch.playbook/branch.fsm.d.ts +169 -0
- package/reference/sdlc/branch.playbook/branch.fsm.js +325 -0
- package/reference/sdlc/branch.playbook/branch.fsm.ts +458 -0
- package/reference/sdlc/branch.playbook/branch.gears.md +44 -0
- package/reference/sdlc/branch.playbook/branch.playbook.d.ts +20 -0
- package/reference/sdlc/branch.playbook/branch.playbook.js +121 -0
- package/reference/sdlc/branch.playbook/branch.playbook.ts +217 -0
- package/reference/sdlc/branch.playbook/branch.registry.d.ts +40 -0
- package/reference/sdlc/branch.playbook/branch.registry.js +63 -0
- package/reference/sdlc/branch.playbook/branch.registry.ts +121 -0
- package/reference/sdlc/code.md +7 -0
- package/reference/sdlc/code.playbook/code.fsm.js +5 -5
- package/reference/sdlc/code.playbook/code.fsm.ts +5 -5
- package/reference/sdlc/code.playbook/code.gears.md +5 -5
- package/reference/sdlc/code.playbook/code.playbook.d.ts +1 -1
- package/reference/sdlc/code.playbook/code.playbook.js +6 -16
- package/reference/sdlc/code.playbook/code.playbook.ts +7 -17
- package/reference/sdlc/code.playbook/playbook-captain.js +4 -1
- package/reference/sdlc/code.playbook/playbook-captain.ts +4 -1
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +16 -2
- package/reference/sdlc/decide.md +2 -0
- package/reference/sdlc/decide.playbook/decide.fsm.js +1 -1
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1 -1
- package/reference/sdlc/decide.playbook/decide.gears.md +2 -2
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +1 -2
- package/reference/sdlc/decide.playbook/decide.playbook.js +8 -22
- package/reference/sdlc/decide.playbook/decide.playbook.ts +8 -25
- package/reference/sdlc/dev.md +45 -4
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +100 -9
- package/reference/sdlc/dev.playbook/dev.fsm.js +245 -28
- package/reference/sdlc/dev.playbook/dev.fsm.ts +381 -37
- package/reference/sdlc/dev.playbook/dev.gears.md +60 -22
- package/reference/sdlc/dev.playbook/dev.playbook.d.ts +1 -1
- package/reference/sdlc/dev.playbook/dev.playbook.js +20 -18
- package/reference/sdlc/dev.playbook/dev.playbook.ts +21 -21
- package/reference/sdlc/dev.playbook/dev.registry.d.ts +1 -1
- package/reference/sdlc/dev.playbook/dev.registry.js +7 -2
- package/reference/sdlc/dev.playbook/dev.registry.ts +7 -2
- package/reference/sdlc/pr.md +108 -0
- package/reference/sdlc/pr.playbook/pr.fsm.d.ts +352 -0
- package/reference/sdlc/pr.playbook/pr.fsm.js +902 -0
- package/reference/sdlc/pr.playbook/pr.fsm.ts +1251 -0
- package/reference/sdlc/pr.playbook/pr.gears.md +155 -0
- package/reference/sdlc/pr.playbook/pr.playbook.d.ts +27 -0
- package/reference/sdlc/pr.playbook/pr.playbook.js +133 -0
- package/reference/sdlc/pr.playbook/pr.playbook.ts +235 -0
- package/reference/sdlc/pr.playbook/pr.registry.d.ts +40 -0
- package/reference/sdlc/pr.playbook/pr.registry.js +68 -0
- package/reference/sdlc/pr.playbook/pr.registry.ts +124 -0
- package/reference/sdlc/review.md +18 -0
- package/reference/sdlc/review.playbook/review.fsm.js +8 -8
- package/reference/sdlc/review.playbook/review.fsm.ts +8 -8
- package/reference/sdlc/review.playbook/review.gears.md +8 -8
- package/reference/sdlc/review.playbook/review.playbook.d.ts +1 -1
- package/reference/sdlc/review.playbook/review.playbook.js +5 -15
- package/reference/sdlc/review.playbook/review.playbook.ts +6 -16
- package/slc/link.md +12 -24
- package/src/runtime.d.ts +2 -0
- package/src/runtime.ts +2 -0
- package/src/xstate-playbook-runtime.d.ts +4 -2
- package/src/xstate-playbook-runtime.js +18 -13
- package/src/xstate-playbook-runtime.ts +26 -14
|
@@ -16,9 +16,9 @@ It coordinates existing playbooks and owns no repository commit itself.
|
|
|
16
16
|
|
|
17
17
|
At the start of `dev` and after each Boss reply, Captain shall relay the development request, relevant discussion context, and any relevant run results to Analyst in quotes (`>`), along with the planning instruction:
|
|
18
18
|
|
|
19
|
-
> > <development-request>
|
|
20
|
-
> > <discussion-context>
|
|
21
|
-
> > <run-results>
|
|
19
|
+
> > Original request: <development-request>
|
|
20
|
+
> > Prior discussion: <discussion-context>
|
|
21
|
+
> > Run results: <run-results>
|
|
22
22
|
>
|
|
23
23
|
> Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.
|
|
24
24
|
> Do not change files or commit while planning or discussing the request.
|
|
@@ -27,6 +27,7 @@ At the start of `dev` and after each Boss reply, Captain shall relay the develop
|
|
|
27
27
|
> - If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.
|
|
28
28
|
> - If implementation can proceed under the existing decisions, choose `code`.
|
|
29
29
|
> - If implementation first requires a new or amended durable decision that the existing specs do not settle, choose `decide then code`.
|
|
30
|
+
> - If the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery, read the issue and its comments as part of the analysis (`gh issue view --comments` with the issue number) and choose `code via pull request` or `decide then code via pull request` in place of `code` or `decide then code`.
|
|
30
31
|
>
|
|
31
32
|
> A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.
|
|
32
33
|
> Do not choose `decide then code` merely because the work is large.
|
|
@@ -36,11 +37,13 @@ Results:
|
|
|
36
37
|
- `discussionComplete`: Analyst concluded the discussion after a Boss reply, with no repository work to follow.
|
|
37
38
|
- `code`: Analyst determined implementation can proceed under the existing decisions. Output shall include `planningResult: <verbatim final text>`.
|
|
38
39
|
- `decideThenCode`: Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle. Output shall include `planningResult: <verbatim final text>`.
|
|
40
|
+
- `codeViaPullRequest`: Analyst determined implementation can proceed under the existing decisions and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.
|
|
41
|
+
- `decideThenCodeViaPullRequest`: Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.
|
|
39
42
|
|
|
40
43
|
Workflow outcomes:
|
|
41
|
-
- The planning result has
|
|
44
|
+
- The planning result has six semantic outcomes: needs Boss reply, discussion complete, code, decide then code, code via pull request, and decide then code via pull request.
|
|
42
45
|
- Each outcome requires affirmative support in Analyst's result; absence of a reason to choose another outcome is not support, and no outcome depends on a fixed presentation format of Analyst's reply.
|
|
43
|
-
- Needs Boss reply uses the standard Boss-question suspension with Analyst's complete response; after Boss replies, `dev` resumes with the
|
|
46
|
+
- Needs Boss reply uses the standard Boss-question suspension with Analyst's complete response; after Boss replies, `dev` resumes with the answer in the same Analyst conversation; the previous question is included only when that conversation must start fresh.
|
|
44
47
|
- Discussion complete is available only after a Boss reply, when any useful analysis has already been presented through needs Boss reply; it completes `dev` without a child call or repository change.
|
|
45
48
|
- `dev` acts on the accepted outcome itself and does not return to the session Captain for another routing decision.
|
|
46
49
|
|
|
@@ -48,25 +51,26 @@ Workflow outcomes:
|
|
|
48
51
|
|
|
49
52
|
### DEV-2
|
|
50
53
|
|
|
51
|
-
When the accepted planning result selects `code`, Captain shall call playbook `code`:
|
|
54
|
+
When the accepted planning result selects `code`, or `branch` succeeds for `code via pull request`, Captain shall call playbook `code`:
|
|
52
55
|
|
|
53
|
-
> > <development-request>
|
|
54
|
-
> > <discussion-context>
|
|
55
|
-
> > <planning-result>
|
|
56
|
+
> > Original request: <development-request>
|
|
57
|
+
> > Prior discussion: <discussion-context>
|
|
58
|
+
> > Planning result: <planning-result>
|
|
56
59
|
|
|
57
60
|
Workflow outcomes:
|
|
58
|
-
- `code` success completes `dev` with the successful `code` result.
|
|
61
|
+
- On a plain path, `code` success completes `dev` with the successful `code` result.
|
|
62
|
+
- On a pull-request path, `code` success provides the exact last `code`-owned commit and the exact final evaluated repository revision from `code`'s canonical structured result and continues with the `pr` call.
|
|
59
63
|
- An authored `code` abort or failure, or a terminal `code` result that does not prove the success required for the selected path, terminates `dev` with that canonical result relayed and no later child call.
|
|
60
64
|
- Any other nested-call error parks `dev` as failed and retains the control-plane error.
|
|
61
|
-
- `dev` consumes commit identities only from each child's canonical structured result, never from player prose.
|
|
65
|
+
- `dev` consumes commit, revision, branch, and pull-request identities only from each child's canonical structured result, never from player prose.
|
|
62
66
|
|
|
63
67
|
### DEV-3
|
|
64
68
|
|
|
65
|
-
When the accepted planning result selects `decide then code`, Captain shall call playbook `decide`:
|
|
69
|
+
When the accepted planning result selects `decide then code`, or `branch` succeeds for `decide then code via pull request`, Captain shall call playbook `decide`:
|
|
66
70
|
|
|
67
|
-
> > <development-request>
|
|
68
|
-
> > <discussion-context>
|
|
69
|
-
> > <planning-result>
|
|
71
|
+
> > Original request: <development-request>
|
|
72
|
+
> > Prior discussion: <discussion-context>
|
|
73
|
+
> > Planning result: <planning-result>
|
|
70
74
|
|
|
71
75
|
Workflow outcomes:
|
|
72
76
|
- `decide` success provides the `decide`-owned commit and the exact evaluated repository revision from `decide`'s canonical structured result and continues with the `code` call.
|
|
@@ -78,14 +82,48 @@ Workflow outcomes:
|
|
|
78
82
|
|
|
79
83
|
When `decide` succeeds, Captain shall call playbook `code`:
|
|
80
84
|
|
|
81
|
-
> > <development-request>
|
|
82
|
-
> > <discussion-context>
|
|
83
|
-
> > <planning-result>
|
|
84
|
-
> > <decide-commit>
|
|
85
|
-
> > <evaluated-revision>
|
|
85
|
+
> > Original request: <development-request>
|
|
86
|
+
> > Prior discussion: <discussion-context>
|
|
87
|
+
> > Planning result: <planning-result>
|
|
88
|
+
> > DECIDE commit: <decide-commit>
|
|
89
|
+
> > Evaluated revision: <evaluated-revision>
|
|
86
90
|
|
|
87
91
|
Workflow outcomes:
|
|
88
|
-
- `code` success completes `dev` with the successful `code` result.
|
|
92
|
+
- On a plain path, `code` success completes `dev` with the successful `code` result.
|
|
93
|
+
- On a pull-request path, `code` success provides the exact last `code`-owned commit and the exact final evaluated repository revision from `code`'s canonical structured result and continues with the `pr` call.
|
|
89
94
|
- An authored `code` abort or failure, or a terminal `code` result that does not prove the success required for the selected path, terminates `dev` with that canonical result relayed and no later child call.
|
|
90
95
|
- Any other nested-call error parks `dev` as failed and retains the control-plane error.
|
|
91
|
-
- `dev` consumes commit identities only from each child's canonical structured result, never from player prose.
|
|
96
|
+
- `dev` consumes commit, revision, branch, and pull-request identities only from each child's canonical structured result, never from player prose.
|
|
97
|
+
|
|
98
|
+
## Pull-request delivery
|
|
99
|
+
|
|
100
|
+
### DEV-5
|
|
101
|
+
|
|
102
|
+
When the accepted planning result selects `code via pull request` or `decide then code via pull request`, Captain shall call playbook `branch`:
|
|
103
|
+
|
|
104
|
+
> > Original request: <development-request>
|
|
105
|
+
> > Prior discussion: <discussion-context>
|
|
106
|
+
> > Planning result: <planning-result>
|
|
107
|
+
|
|
108
|
+
Workflow outcomes:
|
|
109
|
+
- `branch` success provides the exact branch name, the exact base revision, and the issue summary from `branch`'s canonical structured result and continues with the `code` call for code via pull request, or the `decide` call and then the `code` call for decide then code via pull request, each with the same input as its plain path.
|
|
110
|
+
- An authored `branch` abort or failure, or a terminal `branch` result that does not prove that success, terminates `dev` with that canonical result relayed and no later child call.
|
|
111
|
+
- Any other nested-call error parks `dev` as failed and retains the control-plane error.
|
|
112
|
+
- A plain request calls neither `branch` nor `pr`.
|
|
113
|
+
|
|
114
|
+
### DEV-6
|
|
115
|
+
|
|
116
|
+
When `code` succeeds on a pull-request path, Captain shall call playbook `pr`:
|
|
117
|
+
|
|
118
|
+
> > Original request: <development-request>
|
|
119
|
+
> > Issue summary: <issue-summary>
|
|
120
|
+
> > Branch: <branch>
|
|
121
|
+
> > Base revision: <base-revision>
|
|
122
|
+
> > CODE commit: <last-code-commit>
|
|
123
|
+
> > Evaluated revision: <final-evaluated-revision>
|
|
124
|
+
|
|
125
|
+
Workflow outcomes:
|
|
126
|
+
- `pr` success completes `dev` with the successful `pr` result.
|
|
127
|
+
- An authored `pr` abort or failure, or a terminal `pr` result that does not prove the success required for the selected path, terminates `dev` with that canonical result relayed and no later child call.
|
|
128
|
+
- Any other nested-call error parks `dev` as failed and retains the control-plane error.
|
|
129
|
+
- `dev` consumes commit, revision, branch, and pull-request identities only from each child's canonical structured result, never from player prose.
|
|
@@ -11,7 +11,7 @@ export type DevPlaybookHostCapabilities = PlaybookHostConstructionCapabilities &
|
|
|
11
11
|
* override additionally keeps a multiline value inside that quote and drops
|
|
12
12
|
* the optional relays that have no value yet.
|
|
13
13
|
*/
|
|
14
|
-
declare function composePlayerPrompt(input: PlayerInput): string;
|
|
14
|
+
declare function composePlayerPrompt(input: PlayerInput, resuming?: boolean): string;
|
|
15
15
|
export declare const _internal: {
|
|
16
16
|
composePlayerPrompt: typeof composePlayerPrompt;
|
|
17
17
|
VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
// BOSS_REPLY
|
|
10
10
|
// Adjudication: LLM judge for the one Analyst state; planningResult is
|
|
11
11
|
// carried verbatim
|
|
12
|
-
// Nested calls: literal code and
|
|
12
|
+
// Nested calls: literal code, decide, branch, and pr targets through the
|
|
13
|
+
// shared bridge; branch, revision, commit, and pull-request
|
|
14
|
+
// identities enter only from canonical child results
|
|
13
15
|
// Compat: artifact schema 3 / runtime ABI 1
|
|
14
|
-
import { createXStatePlaybookRuntime, snapshotJsonValue, } from '@sublang/playbook/xstate-runtime';
|
|
16
|
+
import { createXStatePlaybookRuntime, composePlayerContinuation, snapshotJsonValue, } from '@sublang/playbook/xstate-runtime';
|
|
15
17
|
import { devMachine, } from './dev.fsm.js';
|
|
16
18
|
const OPTION_KEYS = new Set(['runResults']);
|
|
17
19
|
const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
|
|
18
|
-
const CONTINUATION_PREAMBLE = 'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
|
|
19
20
|
const VERBATIM_PAYLOAD_FIELDS = new Set([
|
|
20
21
|
'planningResult',
|
|
21
22
|
]);
|
|
@@ -54,13 +55,13 @@ function quotedContinuation(value) {
|
|
|
54
55
|
* override additionally keeps a multiline value inside that quote and drops
|
|
55
56
|
* the optional relays that have no value yet.
|
|
56
57
|
*/
|
|
57
|
-
function composePlayerPrompt(input) {
|
|
58
|
+
function composePlayerPrompt(input, resuming = false) {
|
|
58
59
|
const fields = input;
|
|
59
60
|
const template = input.prompt
|
|
60
61
|
.split('\n')
|
|
61
|
-
.filter((line) => !(line === '> <discussion-context>' &&
|
|
62
|
+
.filter((line) => !(line === '> Prior discussion: <discussion-context>' &&
|
|
62
63
|
input.discussionContext.length === 0) &&
|
|
63
|
-
!(line === '> <run-results>' && input.runResults.length === 0))
|
|
64
|
+
!(line === '> Run results: <run-results>' && input.runResults.length === 0))
|
|
64
65
|
.join('\n');
|
|
65
66
|
const body = template.replace(PLACEHOLDER, (match, token, offset, source) => {
|
|
66
67
|
const value = fields[placeholderField(token)];
|
|
@@ -68,18 +69,9 @@ function composePlayerPrompt(input) {
|
|
|
68
69
|
return match;
|
|
69
70
|
const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
|
|
70
71
|
const literal = source.slice(lineStart, offset);
|
|
71
|
-
return literal
|
|
72
|
+
return literal.startsWith('> ') ? quotedContinuation(value) : value;
|
|
72
73
|
});
|
|
73
|
-
|
|
74
|
-
input.bossReply === undefined) {
|
|
75
|
-
return body;
|
|
76
|
-
}
|
|
77
|
-
return [
|
|
78
|
-
CONTINUATION_PREAMBLE,
|
|
79
|
-
`Boss question:\n${input.pendingBossQuestion.question}`,
|
|
80
|
-
`Boss reply:\n${input.bossReply}`,
|
|
81
|
-
body,
|
|
82
|
-
].join('\n\n');
|
|
74
|
+
return composePlayerContinuation(input, body, resuming);
|
|
83
75
|
}
|
|
84
76
|
export const _internal = {
|
|
85
77
|
composePlayerPrompt,
|
|
@@ -126,6 +118,16 @@ const runtimeSpec = {
|
|
|
126
118
|
fields: { planningResult: 'presentation' },
|
|
127
119
|
repositoryDisposition: 'unchanged',
|
|
128
120
|
},
|
|
121
|
+
// DR-050: reading a GitHub issue during planning is not an effect, so
|
|
122
|
+
// the two pull-request outcomes stay `unchanged` like the plain ones.
|
|
123
|
+
codeViaPullRequest: {
|
|
124
|
+
fields: { planningResult: 'presentation' },
|
|
125
|
+
repositoryDisposition: 'unchanged',
|
|
126
|
+
},
|
|
127
|
+
decideThenCodeViaPullRequest: {
|
|
128
|
+
fields: { planningResult: 'presentation' },
|
|
129
|
+
repositoryDisposition: 'unchanged',
|
|
130
|
+
},
|
|
129
131
|
needsBossReply: {
|
|
130
132
|
fields: { question: 'presentation' },
|
|
131
133
|
repositoryDisposition: 'unchanged',
|
|
@@ -133,7 +135,7 @@ const runtimeSpec = {
|
|
|
133
135
|
},
|
|
134
136
|
},
|
|
135
137
|
},
|
|
136
|
-
composePlayerPrompt: (input) => composePlayerPrompt(input),
|
|
138
|
+
composePlayerPrompt: (input, _identity, resuming) => composePlayerPrompt(input, resuming),
|
|
137
139
|
verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
|
|
138
140
|
controlContextFields: [],
|
|
139
141
|
unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
|
|
@@ -9,11 +9,14 @@
|
|
|
9
9
|
// BOSS_REPLY
|
|
10
10
|
// Adjudication: LLM judge for the one Analyst state; planningResult is
|
|
11
11
|
// carried verbatim
|
|
12
|
-
// Nested calls: literal code and
|
|
12
|
+
// Nested calls: literal code, decide, branch, and pr targets through the
|
|
13
|
+
// shared bridge; branch, revision, commit, and pull-request
|
|
14
|
+
// identities enter only from canonical child results
|
|
13
15
|
// Compat: artifact schema 3 / runtime ABI 1
|
|
14
16
|
|
|
15
17
|
import {
|
|
16
18
|
createXStatePlaybookRuntime,
|
|
19
|
+
composePlayerContinuation,
|
|
17
20
|
snapshotJsonValue,
|
|
18
21
|
type PlaybookPlayerInput,
|
|
19
22
|
type XStatePlaybookRuntimeFactory,
|
|
@@ -85,8 +88,6 @@ export type DevPlaybookHostCapabilities =
|
|
|
85
88
|
|
|
86
89
|
const OPTION_KEYS = new Set(['runResults']);
|
|
87
90
|
const PLACEHOLDER = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g;
|
|
88
|
-
const CONTINUATION_PREAMBLE =
|
|
89
|
-
'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
|
|
90
91
|
|
|
91
92
|
const VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string> = new Set([
|
|
92
93
|
'planningResult',
|
|
@@ -133,17 +134,17 @@ function quotedContinuation(value: string): string {
|
|
|
133
134
|
* override additionally keeps a multiline value inside that quote and drops
|
|
134
135
|
* the optional relays that have no value yet.
|
|
135
136
|
*/
|
|
136
|
-
function composePlayerPrompt(input: PlayerInput): string {
|
|
137
|
+
function composePlayerPrompt(input: PlayerInput, resuming = false): string {
|
|
137
138
|
const fields = input as unknown as Record<string, unknown>;
|
|
138
139
|
const template = input.prompt
|
|
139
140
|
.split('\n')
|
|
140
141
|
.filter(
|
|
141
142
|
(line) =>
|
|
142
143
|
!(
|
|
143
|
-
line === '> <discussion-context>' &&
|
|
144
|
+
line === '> Prior discussion: <discussion-context>' &&
|
|
144
145
|
input.discussionContext.length === 0
|
|
145
146
|
) &&
|
|
146
|
-
!(line === '> <run-results>' && input.runResults.length === 0),
|
|
147
|
+
!(line === '> Run results: <run-results>' && input.runResults.length === 0),
|
|
147
148
|
)
|
|
148
149
|
.join('\n');
|
|
149
150
|
const body = template.replace(
|
|
@@ -153,21 +154,10 @@ function composePlayerPrompt(input: PlayerInput): string {
|
|
|
153
154
|
if (typeof value !== 'string') return match;
|
|
154
155
|
const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
|
|
155
156
|
const literal = source.slice(lineStart, offset);
|
|
156
|
-
return literal
|
|
157
|
+
return literal.startsWith('> ') ? quotedContinuation(value) : value;
|
|
157
158
|
},
|
|
158
159
|
);
|
|
159
|
-
|
|
160
|
-
input.pendingBossQuestion === undefined ||
|
|
161
|
-
input.bossReply === undefined
|
|
162
|
-
) {
|
|
163
|
-
return body;
|
|
164
|
-
}
|
|
165
|
-
return [
|
|
166
|
-
CONTINUATION_PREAMBLE,
|
|
167
|
-
`Boss question:\n${input.pendingBossQuestion.question}`,
|
|
168
|
-
`Boss reply:\n${input.bossReply}`,
|
|
169
|
-
body,
|
|
170
|
-
].join('\n\n');
|
|
160
|
+
return composePlayerContinuation(input, body, resuming);
|
|
171
161
|
}
|
|
172
162
|
|
|
173
163
|
export const _internal = {
|
|
@@ -217,6 +207,16 @@ const runtimeSpec = {
|
|
|
217
207
|
fields: { planningResult: 'presentation' },
|
|
218
208
|
repositoryDisposition: 'unchanged',
|
|
219
209
|
},
|
|
210
|
+
// DR-050: reading a GitHub issue during planning is not an effect, so
|
|
211
|
+
// the two pull-request outcomes stay `unchanged` like the plain ones.
|
|
212
|
+
codeViaPullRequest: {
|
|
213
|
+
fields: { planningResult: 'presentation' },
|
|
214
|
+
repositoryDisposition: 'unchanged',
|
|
215
|
+
},
|
|
216
|
+
decideThenCodeViaPullRequest: {
|
|
217
|
+
fields: { planningResult: 'presentation' },
|
|
218
|
+
repositoryDisposition: 'unchanged',
|
|
219
|
+
},
|
|
220
220
|
needsBossReply: {
|
|
221
221
|
fields: { question: 'presentation' },
|
|
222
222
|
repositoryDisposition: 'unchanged',
|
|
@@ -224,8 +224,8 @@ const runtimeSpec = {
|
|
|
224
224
|
},
|
|
225
225
|
},
|
|
226
226
|
},
|
|
227
|
-
composePlayerPrompt: (input: PlaybookPlayerInput) =>
|
|
228
|
-
composePlayerPrompt(input as PlayerInput),
|
|
227
|
+
composePlayerPrompt: (input: PlaybookPlayerInput, _identity, resuming) =>
|
|
228
|
+
composePlayerPrompt(input as PlayerInput, resuming),
|
|
229
229
|
verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
|
|
230
230
|
controlContextFields: [],
|
|
231
231
|
unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
|
|
@@ -29,7 +29,7 @@ export interface DevPlaybookRegistryEntry {
|
|
|
29
29
|
export declare const devStateCountLabels: {
|
|
30
30
|
readonly planAnalysis: "planning round";
|
|
31
31
|
};
|
|
32
|
-
export declare const devCopyPasteGuardNames: readonly ["code", "decideThenCode"];
|
|
32
|
+
export declare const devCopyPasteGuardNames: readonly ["code", "decideThenCode", "codeViaPullRequest", "decideThenCodeViaPullRequest"];
|
|
33
33
|
export declare function devSavedCountsLine(counts: {
|
|
34
34
|
interruptions: number;
|
|
35
35
|
copyPastes: number;
|
|
@@ -7,7 +7,12 @@ import createPlaybookRuntime from './dev.playbook.js';
|
|
|
7
7
|
export const devStateCountLabels = {
|
|
8
8
|
planAnalysis: 'planning round',
|
|
9
9
|
};
|
|
10
|
-
export const devCopyPasteGuardNames = [
|
|
10
|
+
export const devCopyPasteGuardNames = [
|
|
11
|
+
'code',
|
|
12
|
+
'decideThenCode',
|
|
13
|
+
'codeViaPullRequest',
|
|
14
|
+
'decideThenCodeViaPullRequest',
|
|
15
|
+
];
|
|
11
16
|
function countNoun(count, singular, plural = `${singular}s`) {
|
|
12
17
|
return `${count} ${count === 1 ? singular : plural}`;
|
|
13
18
|
}
|
|
@@ -44,7 +49,7 @@ export function validateDevOptions(optionSlice) {
|
|
|
44
49
|
export const devPlaybookRegistryEntry = {
|
|
45
50
|
id: 'dev',
|
|
46
51
|
command: 'dev',
|
|
47
|
-
intent: 'analyze a development request that needs planning before choosing direct implementation or a durable decision first',
|
|
52
|
+
intent: 'analyze a development request that needs planning before choosing direct implementation or a durable decision first, delivered through a pull request when the request names a GitHub issue or asks for one',
|
|
48
53
|
artifactSchema: 3,
|
|
49
54
|
runtimeProfile: Object.freeze({
|
|
50
55
|
kind: 'shared-factory',
|
|
@@ -46,7 +46,12 @@ export const devStateCountLabels = {
|
|
|
46
46
|
planAnalysis: 'planning round',
|
|
47
47
|
} as const;
|
|
48
48
|
|
|
49
|
-
export const devCopyPasteGuardNames = [
|
|
49
|
+
export const devCopyPasteGuardNames = [
|
|
50
|
+
'code',
|
|
51
|
+
'decideThenCode',
|
|
52
|
+
'codeViaPullRequest',
|
|
53
|
+
'decideThenCodeViaPullRequest',
|
|
54
|
+
] as const;
|
|
50
55
|
|
|
51
56
|
function countNoun(
|
|
52
57
|
count: number,
|
|
@@ -99,7 +104,7 @@ export const devPlaybookRegistryEntry: DevPlaybookRegistryEntry = {
|
|
|
99
104
|
id: 'dev',
|
|
100
105
|
command: 'dev',
|
|
101
106
|
intent:
|
|
102
|
-
'analyze a development request that needs planning before choosing direct implementation or a durable decision first',
|
|
107
|
+
'analyze a development request that needs planning before choosing direct implementation or a durable decision first, delivered through a pull request when the request names a GitHub issue or asks for one',
|
|
103
108
|
artifactSchema: 3,
|
|
104
109
|
runtimeProfile: Object.freeze({
|
|
105
110
|
kind: 'shared-factory',
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
|
2
|
+
<!-- SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai> -->
|
|
3
|
+
<!-- AI agents: do not edit this file without human approval. -->
|
|
4
|
+
|
|
5
|
+
# PR
|
|
6
|
+
|
|
7
|
+
Roles:
|
|
8
|
+
|
|
9
|
+
- Coder
|
|
10
|
+
|
|
11
|
+
The caller supplies:
|
|
12
|
+
|
|
13
|
+
- the original request, including the issue it names, if any;
|
|
14
|
+
- the issue summary;
|
|
15
|
+
- the branch to deliver and the base revision it was created from;
|
|
16
|
+
- the last `code`-owned commit and the exact evaluated repository revision;
|
|
17
|
+
- optional relevant context.
|
|
18
|
+
|
|
19
|
+
`pr` delivers a reviewed branch into the repository default branch through a GitHub pull request: it publishes the branch, waits for the pull request's checks, fixes red checks through playbook `code` no more than once, and merges.
|
|
20
|
+
It changes no files and owns no repository commit; the one fix it may request is owned by `code`.
|
|
21
|
+
The check waits, the fix publication, the merge, and the local update are mechanical steps: each runs one fixed command whose exit status alone decides its two outcomes, reads no conversation, and produces no prose.
|
|
22
|
+
`gh` infers the pull request from the checked-out branch, so no mechanical step needs a runtime value.
|
|
23
|
+
|
|
24
|
+
When the caller gives its input, Captain shall relay the complete caller input in quotes (`>`) to Coder, along with the following instruction:
|
|
25
|
+
|
|
26
|
+
> Original request: <caller-input>
|
|
27
|
+
|
|
28
|
+
```markdown
|
|
29
|
+
Publish the branch and open its pull request without changing any file or making any commit.
|
|
30
|
+
Confirm that the working tree is clean and that the checked-out branch is the branch to deliver, not the repository default branch.
|
|
31
|
+
Push the branch to the repository's GitHub remote with its upstream set; never force-push.
|
|
32
|
+
If an open pull request for this branch already exists, use it; otherwise open one against the repository default branch with `gh pr create`.
|
|
33
|
+
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.
|
|
34
|
+
Report the pull request number and URL exactly.
|
|
35
|
+
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.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The result has two semantic outcomes: opened and not published.
|
|
39
|
+
Each outcome requires affirmative support in Coder's result, and no outcome depends on a fixed presentation format of Coder's reply.
|
|
40
|
+
Every outcome keeps the repository exact: pushing a branch and opening a pull request change neither HEAD's commit nor the working tree.
|
|
41
|
+
For not published, `pr` fails and reports Coder's complete result with its reason to its caller.
|
|
42
|
+
|
|
43
|
+
When the pull request is open and no fix has been attempted, Captain shall wait for the pull request's checks by running exactly the following command in the repository, with no other action and without reading its output:
|
|
44
|
+
|
|
45
|
+
> n=0
|
|
46
|
+
> while gh pr checks 2>&1 | grep -q 'no checks reported'; do
|
|
47
|
+
> n=$((n + 1))
|
|
48
|
+
> [ "$n" -ge 6 ] && exit 0
|
|
49
|
+
> sleep 10
|
|
50
|
+
> done
|
|
51
|
+
> gh pr checks --watch --fail-fast >/dev/null 2>&1
|
|
52
|
+
|
|
53
|
+
The wait has exactly two outcomes decided by the command's exit status alone: checks passed on status zero and checks failed otherwise.
|
|
54
|
+
A pull request whose repository still reports no checks after a brief wait for them to register counts as passed.
|
|
55
|
+
|
|
56
|
+
When the checks fail before any fix attempt, Captain shall call playbook `code` with the original request, the pull request, and the coding request in quotes (`>`):
|
|
57
|
+
|
|
58
|
+
> Original request: <caller-input>
|
|
59
|
+
> Pull request: <pull-request-url>
|
|
60
|
+
> 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.
|
|
61
|
+
|
|
62
|
+
`pr` makes no more than one fix attempt.
|
|
63
|
+
Only after `code` succeeds shall `pr` publish the fix and wait for the checks again.
|
|
64
|
+
When `code` returns an authored abort or failure, or a terminal result that does not prove its success, `pr` shall fail relaying that canonical result and shall leave the pull request open.
|
|
65
|
+
When the nested `code` call fails outside that authored result contract, `pr` shall park as failed and retain the control-plane error instead of reporting an authored outcome.
|
|
66
|
+
|
|
67
|
+
When `code` succeeds, Captain shall publish the fix to the pull request by running exactly the following command in the repository, with no other action and without reading its output:
|
|
68
|
+
|
|
69
|
+
> git push || exit 1
|
|
70
|
+
> n=0
|
|
71
|
+
> until [ "$(gh pr view --json headRefOid --jq .headRefOid 2>/dev/null)" = "$(git rev-parse HEAD)" ]; do
|
|
72
|
+
> n=$((n + 1))
|
|
73
|
+
> [ "$n" -ge 12 ] && exit 1
|
|
74
|
+
> sleep 5
|
|
75
|
+
> done
|
|
76
|
+
|
|
77
|
+
The publication has exactly two outcomes decided by the exit status alone: fix published on status zero, once the pull request's head is the pushed commit, and fix not published otherwise.
|
|
78
|
+
Fix not published is an authored failure that leaves the pull request open.
|
|
79
|
+
|
|
80
|
+
When the fix is published, Captain shall wait for the pull request's checks again by running exactly the same command as the first wait in the repository, with no other action and without reading its output:
|
|
81
|
+
|
|
82
|
+
> n=0
|
|
83
|
+
> while gh pr checks 2>&1 | grep -q 'no checks reported'; do
|
|
84
|
+
> n=$((n + 1))
|
|
85
|
+
> [ "$n" -ge 6 ] && exit 0
|
|
86
|
+
> sleep 10
|
|
87
|
+
> done
|
|
88
|
+
> gh pr checks --watch --fail-fast >/dev/null 2>&1
|
|
89
|
+
|
|
90
|
+
This wait has exactly two outcomes decided by the exit status alone: checks passed on status zero and checks still failing otherwise.
|
|
91
|
+
Checks still failing is an authored failure that leaves the pull request open; there is no second fix attempt.
|
|
92
|
+
|
|
93
|
+
When the checks pass, before or after the one fix attempt, Captain shall merge the pull request by running exactly the following command in the repository, with no other action and without reading its output:
|
|
94
|
+
|
|
95
|
+
> gh pr merge --merge --delete-branch --match-head-commit "$(git rev-parse HEAD)"
|
|
96
|
+
|
|
97
|
+
The merge creates a merge commit on the repository default branch, deletes the remote and local branch, and checks out the local default branch; GitHub closes the linked issue on merge.
|
|
98
|
+
The merge has exactly two outcomes decided by the exit status alone: merged on status zero and merge refused otherwise.
|
|
99
|
+
Merge refused is an authored failure that leaves the pull request in the state GitHub reports: GitHub refused the merge for a conflict, a branch protection, a forbidden merge method, or a head that moved, or the merge landed but the local switch to the default branch or the branch deletion failed.
|
|
100
|
+
|
|
101
|
+
When the pull request is merged, Captain shall bring the local default branch to the merged head by running exactly the following command in the repository, with no other action and without reading its output:
|
|
102
|
+
|
|
103
|
+
> git pull --ff-only
|
|
104
|
+
|
|
105
|
+
The update has exactly two outcomes decided by the exit status alone: local default updated on status zero and local default not updated otherwise.
|
|
106
|
+
Both complete `pr`: the pull request is merged either way, and the result states whether the local default branch was fast-forwarded to the merged head.
|
|
107
|
+
|
|
108
|
+
On completion, `pr` returns the pull request number and URL, the fact that the pull request is merged, and whether the local default branch was fast-forwarded to the merged head.
|