@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
|
@@ -2599,6 +2599,9 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
2599
2599
|
if (!ledger) {
|
|
2600
2600
|
throw new Error(`${frameLabel(frame)} resolved absent session player ${JSON.stringify(binding.playerId)}`);
|
|
2601
2601
|
}
|
|
2602
|
+
if (options.freshPrompt !== undefined && typeof options.freshPrompt !== 'string') {
|
|
2603
|
+
throw new TypeError('player freshPrompt must be a string');
|
|
2604
|
+
}
|
|
2602
2605
|
const expectedResume = ledger.resumeToken ?? false;
|
|
2603
2606
|
if (options.resume !== expectedResume) {
|
|
2604
2607
|
throw new Error(`${frameLabel(frame)} player continuation changed before dispatch`);
|
|
@@ -2625,7 +2628,7 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
2625
2628
|
const call = async (resume) => {
|
|
2626
2629
|
await continuity?.beforeCall(binding.playerId);
|
|
2627
2630
|
signal.throwIfAborted();
|
|
2628
|
-
const raw = await trackHostCall(frame, classifySettingsCall(() => context.callPlayer(binding.playerId, prompt, { resume, settings })));
|
|
2631
|
+
const raw = await trackHostCall(frame, classifySettingsCall(() => context.callPlayer(binding.playerId, resume === false ? options.freshPrompt ?? prompt : prompt, { resume, settings })));
|
|
2629
2632
|
hostResolved = true;
|
|
2630
2633
|
return raw;
|
|
2631
2634
|
};
|
|
@@ -4361,6 +4361,9 @@ export function createPlaybookCaptainShell(
|
|
|
4361
4361
|
`${frameLabel(frame)} resolved absent session player ${JSON.stringify(binding.playerId)}`,
|
|
4362
4362
|
);
|
|
4363
4363
|
}
|
|
4364
|
+
if (options.freshPrompt !== undefined && typeof options.freshPrompt !== 'string') {
|
|
4365
|
+
throw new TypeError('player freshPrompt must be a string');
|
|
4366
|
+
}
|
|
4364
4367
|
const expectedResume = ledger.resumeToken ?? false;
|
|
4365
4368
|
if (options.resume !== expectedResume) {
|
|
4366
4369
|
throw new Error(
|
|
@@ -4394,7 +4397,7 @@ export function createPlaybookCaptainShell(
|
|
|
4394
4397
|
const raw = await trackHostCall(
|
|
4395
4398
|
frame,
|
|
4396
4399
|
classifySettingsCall(() =>
|
|
4397
|
-
context.callPlayer(binding.playerId, prompt, { resume, settings }),
|
|
4400
|
+
context.callPlayer(binding.playerId, resume === false ? options.freshPrompt ?? prompt : prompt, { resume, settings }),
|
|
4398
4401
|
),
|
|
4399
4402
|
);
|
|
4400
4403
|
hostResolved = true;
|
|
@@ -78,8 +78,8 @@ players:
|
|
|
78
78
|
# `roles` are launcher-owned; every other key is that playbook's option
|
|
79
79
|
# slice.
|
|
80
80
|
#
|
|
81
|
-
# Every manifest role is bound explicitly. CODE, REVIEW, and
|
|
82
|
-
# intentionally reuse the same two stable players, while DEV keeps its own
|
|
81
|
+
# Every manifest role is bound explicitly. CODE, REVIEW, DECIDE, BRANCH, and
|
|
82
|
+
# PR intentionally reuse the same two stable players, while DEV keeps its own
|
|
83
83
|
# Analyst; change an id to isolate its conversation.
|
|
84
84
|
playbooks:
|
|
85
85
|
code:
|
|
@@ -103,3 +103,17 @@ playbooks:
|
|
|
103
103
|
from: "@sublang/playbook/dev/registry"
|
|
104
104
|
roles:
|
|
105
105
|
analyst: dev.analyst
|
|
106
|
+
|
|
107
|
+
# Pull-request delivery for DEV (DR-050). BRANCH and PR bind Coder to the
|
|
108
|
+
# same dev.coder player as CODE and REVIEW on purpose: the Coder that read
|
|
109
|
+
# the issue and named the branch makes the commits and then describes
|
|
110
|
+
# them in the pull request. Change an id to isolate its conversation.
|
|
111
|
+
branch:
|
|
112
|
+
from: "@sublang/playbook/branch/registry"
|
|
113
|
+
roles:
|
|
114
|
+
coder: dev.coder
|
|
115
|
+
|
|
116
|
+
pr:
|
|
117
|
+
from: "@sublang/playbook/pr/registry"
|
|
118
|
+
roles:
|
|
119
|
+
coder: dev.coder
|
package/reference/sdlc/decide.md
CHANGED
|
@@ -15,6 +15,8 @@ When the caller gives a topic, Captain shall relay the complete topic in quotes
|
|
|
15
15
|
Captain shall not wait for either proposal before requesting the other.
|
|
16
16
|
Captain shall give the player bound to each role the following instruction:
|
|
17
17
|
|
|
18
|
+
> Original topic: <caller-topic>
|
|
19
|
+
|
|
18
20
|
```markdown
|
|
19
21
|
Assess whether the topic is better expressed as a few spec items under @specs/packages/ or requires one or more DRs under @specs/decisions/.
|
|
20
22
|
Propose your design.
|
|
@@ -7,7 +7,7 @@ import { assign, fromPromise, setup } from 'xstate';
|
|
|
7
7
|
export const concurrentRoleSets = [['coder', 'reviewer']];
|
|
8
8
|
const NEEDS_BOSS_REPLY_DESCRIPTION = "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.";
|
|
9
9
|
const INDEPENDENT_PROPOSAL_PROMPT = [
|
|
10
|
-
'> <caller-topic>',
|
|
10
|
+
'> Original topic: <caller-topic>',
|
|
11
11
|
'',
|
|
12
12
|
'Assess whether the topic is better expressed as a few spec items under @specs/packages/ or requires one or more DRs under @specs/decisions/.',
|
|
13
13
|
'Propose your design.',
|
|
@@ -148,7 +148,7 @@ const NEEDS_BOSS_REPLY_DESCRIPTION =
|
|
|
148
148
|
"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>`.";
|
|
149
149
|
|
|
150
150
|
const INDEPENDENT_PROPOSAL_PROMPT = [
|
|
151
|
-
'> <caller-topic>',
|
|
151
|
+
'> Original topic: <caller-topic>',
|
|
152
152
|
'',
|
|
153
153
|
'Assess whether the topic is better expressed as a few spec items under @specs/packages/ or requires one or more DRs under @specs/decisions/.',
|
|
154
154
|
'Propose your design.',
|
|
@@ -22,7 +22,7 @@ Parallel group: independent-proposals
|
|
|
22
22
|
|
|
23
23
|
When the caller gives a topic, Captain shall relay the complete topic to Coder in quotes and prompt Coder:
|
|
24
24
|
|
|
25
|
-
> > <caller-topic>
|
|
25
|
+
> > Original topic: <caller-topic>
|
|
26
26
|
>
|
|
27
27
|
> Assess whether the topic is better expressed as a few spec items under @specs/packages/ or requires one or more DRs under @specs/decisions/.
|
|
28
28
|
> Propose your design.
|
|
@@ -39,7 +39,7 @@ Parallel group: independent-proposals
|
|
|
39
39
|
|
|
40
40
|
When the caller gives a topic, Captain shall relay the complete topic to Reviewer in quotes and prompt Reviewer:
|
|
41
41
|
|
|
42
|
-
> > <caller-topic>
|
|
42
|
+
> > Original topic: <caller-topic>
|
|
43
43
|
>
|
|
44
44
|
> Assess whether the topic is better expressed as a few spec items under @specs/packages/ or requires one or more DRs under @specs/decisions/.
|
|
45
45
|
> Propose your design.
|
|
@@ -10,7 +10,7 @@ export interface DecidePlaybookRuntimeConstruction {
|
|
|
10
10
|
readonly hostCapabilities: DecidePlaybookHostCapabilities;
|
|
11
11
|
}
|
|
12
12
|
type PromptIdentity = (roleId: RoleId) => string;
|
|
13
|
-
declare function composePlayerPrompt(input: PlayerInput, promptIdentity: PromptIdentity): string;
|
|
13
|
+
declare function composePlayerPrompt(input: PlayerInput, promptIdentity: PromptIdentity, resuming?: boolean): string;
|
|
14
14
|
declare function requiredFieldsFor(description: string): string[];
|
|
15
15
|
declare function extractJson(raw: string): Record<string, unknown> | null;
|
|
16
16
|
declare function buildClassifierPrompt(text: string, ctx: {
|
|
@@ -67,7 +67,6 @@ export declare const _internal: {
|
|
|
67
67
|
VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
|
|
68
68
|
BOSS_INTERRUPT_TARGETS: readonly ["independentProposals"];
|
|
69
69
|
UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string>;
|
|
70
|
-
CONTINUATION_PREAMBLE: string;
|
|
71
70
|
TELEMETRY_TOPIC: string;
|
|
72
71
|
};
|
|
73
72
|
export default createPlaybookRuntime;
|
|
@@ -19,7 +19,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
19
19
|
import PQueue from 'p-queue';
|
|
20
20
|
import { createActor, fromPromise } from 'xstate';
|
|
21
21
|
import { createAcceptedOutcomeConsumer, } from '../../../src/accepted-outcome.js';
|
|
22
|
-
import { assertJsonSafe, assertPlaybookEffectLedger, assertPlaybookRuntimeSnapshot, combineAbortSignals, createNestedPlaybookBridge, detachPersistedMachineSnapshot, normalizeError, normalizePlaybookSnapshot, PlaybookSemanticCandidateStructureError, reconcilePlaybookSemanticEvidence, renderGovernedOutcomeContract, snapshotJsonValue, snapshotPlaybookSession, terminalOutcomesFromMachine, validatePlayerResult, waitForPlaybookQuiescence, } from '../../../src/xstate-runtime.js';
|
|
22
|
+
import { assertJsonSafe, assertPlaybookEffectLedger, assertPlaybookRuntimeSnapshot, combineAbortSignals, composePlayerContinuation, createNestedPlaybookBridge, detachPersistedMachineSnapshot, normalizeError, normalizePlaybookSnapshot, PlaybookSemanticCandidateStructureError, reconcilePlaybookSemanticEvidence, renderGovernedOutcomeContract, snapshotJsonValue, snapshotPlaybookSession, terminalOutcomesFromMachine, validatePlayerResult, waitForPlaybookQuiescence, } from '../../../src/xstate-runtime.js';
|
|
23
23
|
import decideMachine from './decide.fsm.js';
|
|
24
24
|
function snapshotDecideRuntimeOptions(value) {
|
|
25
25
|
const captured = snapshotJsonValue(value, 'DECIDE runtime options');
|
|
@@ -125,7 +125,6 @@ const TELEMETRY_TOPIC = 'playbook.fsm.state';
|
|
|
125
125
|
const TRACE_TOPIC = 'playbook.trace';
|
|
126
126
|
const UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID = 'reconcile:unresolved-effect';
|
|
127
127
|
const UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID = 'abandon:unresolved-effect';
|
|
128
|
-
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.';
|
|
129
128
|
const PLACEHOLDER_FIELDS = [
|
|
130
129
|
['<caller-topic>', 'callerTopic'],
|
|
131
130
|
['<reviewer-proposal>', 'reviewerProposal'],
|
|
@@ -135,19 +134,7 @@ const VERBATIM_PAYLOAD_FIELDS = new Set([
|
|
|
135
134
|
'reviewerProposal',
|
|
136
135
|
'coderOutput',
|
|
137
136
|
]);
|
|
138
|
-
function composePlayerPrompt(input, promptIdentity) {
|
|
139
|
-
const blocks = [];
|
|
140
|
-
if (input.pendingBossQuestion && input.bossReply !== undefined) {
|
|
141
|
-
blocks.push([
|
|
142
|
-
CONTINUATION_PREAMBLE,
|
|
143
|
-
'',
|
|
144
|
-
'Boss question:',
|
|
145
|
-
input.pendingBossQuestion.question,
|
|
146
|
-
'',
|
|
147
|
-
'Boss reply:',
|
|
148
|
-
input.bossReply,
|
|
149
|
-
].join('\n'));
|
|
150
|
-
}
|
|
137
|
+
function composePlayerPrompt(input, promptIdentity, resuming = false) {
|
|
151
138
|
const replacements = new Map();
|
|
152
139
|
for (const [placeholder, field] of PLACEHOLDER_FIELDS) {
|
|
153
140
|
const value = input[field];
|
|
@@ -171,8 +158,7 @@ function composePlayerPrompt(input, promptIdentity) {
|
|
|
171
158
|
? value.replaceAll('\n', '\n> ')
|
|
172
159
|
: value;
|
|
173
160
|
});
|
|
174
|
-
|
|
175
|
-
return blocks.join('\n\n');
|
|
161
|
+
return composePlayerContinuation(input, body, resuming);
|
|
176
162
|
}
|
|
177
163
|
// A `result` description names required payload fields in its
|
|
178
164
|
// "Output shall include ..." sentence.
|
|
@@ -883,7 +869,7 @@ function createDecidePlaybookRuntime(options, deferredEffects) {
|
|
|
883
869
|
};
|
|
884
870
|
const resolvedPlayerId = (roleId) => requireSessionIdentity().roleBindings?.[roleId]?.playerId;
|
|
885
871
|
const promptIdentity = (roleId) => requireSessionIdentity().roleBindings?.[roleId]?.promptIdentity ?? roleId;
|
|
886
|
-
const composeInvocationPrompt = (input) => {
|
|
872
|
+
const composeInvocationPrompt = (input, resuming = false) => {
|
|
887
873
|
let active = true;
|
|
888
874
|
const lookup = (roleId) => {
|
|
889
875
|
if (!active) {
|
|
@@ -895,7 +881,7 @@ function createDecidePlaybookRuntime(options, deferredEffects) {
|
|
|
895
881
|
return promptIdentity(roleId);
|
|
896
882
|
};
|
|
897
883
|
try {
|
|
898
|
-
return composePlayerPrompt(input, lookup);
|
|
884
|
+
return composePlayerPrompt(input, lookup, resuming);
|
|
899
885
|
}
|
|
900
886
|
finally {
|
|
901
887
|
active = false;
|
|
@@ -1339,7 +1325,7 @@ function createDecidePlaybookRuntime(options, deferredEffects) {
|
|
|
1339
1325
|
const roleId = input.role;
|
|
1340
1326
|
const playerId = resolvedPlayerId(roleId);
|
|
1341
1327
|
const playerKey = continuationKey(roleId, playerId);
|
|
1342
|
-
const
|
|
1328
|
+
const freshPrompt = composeInvocationPrompt(input);
|
|
1343
1329
|
let resume;
|
|
1344
1330
|
try {
|
|
1345
1331
|
signal.throwIfAborted();
|
|
@@ -1353,6 +1339,7 @@ function createDecidePlaybookRuntime(options, deferredEffects) {
|
|
|
1353
1339
|
latchControlPlaneError(error, signal);
|
|
1354
1340
|
throw error;
|
|
1355
1341
|
}
|
|
1342
|
+
const prompt = resume === false ? freshPrompt : composeInvocationPrompt(input, true);
|
|
1356
1343
|
const callId = continuation?.callId ?? `player-${++playerCallSequence}`;
|
|
1357
1344
|
const identity = {
|
|
1358
1345
|
stateId: input.stateId,
|
|
@@ -1384,7 +1371,7 @@ function createDecidePlaybookRuntime(options, deferredEffects) {
|
|
|
1384
1371
|
let rawResult;
|
|
1385
1372
|
try {
|
|
1386
1373
|
signal.throwIfAborted();
|
|
1387
|
-
const boundary = Promise.resolve(requirePorts().callPlayer(roleId, prompt, signal, { resume }));
|
|
1374
|
+
const boundary = Promise.resolve(requirePorts().callPlayer(roleId, prompt, signal, { resume, ...(prompt === freshPrompt ? {} : { freshPrompt }) }));
|
|
1388
1375
|
rawResult = await boundary;
|
|
1389
1376
|
// An XState sibling cancellation does not cancel an arbitrary coder
|
|
1390
1377
|
// promise. Re-check before a late resolution can mutate continuity or
|
|
@@ -3586,7 +3573,6 @@ export const _internal = {
|
|
|
3586
3573
|
VERBATIM_PAYLOAD_FIELDS,
|
|
3587
3574
|
BOSS_INTERRUPT_TARGETS,
|
|
3588
3575
|
UNFINISHED_FINAL_STATE_IDS,
|
|
3589
|
-
CONTINUATION_PREAMBLE,
|
|
3590
3576
|
TELEMETRY_TOPIC,
|
|
3591
3577
|
};
|
|
3592
3578
|
export default createPlaybookRuntime;
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
assertPlaybookEffectLedger,
|
|
33
33
|
assertPlaybookRuntimeSnapshot,
|
|
34
34
|
combineAbortSignals,
|
|
35
|
+
composePlayerContinuation,
|
|
35
36
|
createNestedPlaybookBridge,
|
|
36
37
|
detachPersistedMachineSnapshot,
|
|
37
38
|
normalizeError,
|
|
@@ -286,8 +287,6 @@ const UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID =
|
|
|
286
287
|
const UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID =
|
|
287
288
|
'abandon:unresolved-effect';
|
|
288
289
|
|
|
289
|
-
const CONTINUATION_PREAMBLE =
|
|
290
|
-
'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
|
|
291
290
|
|
|
292
291
|
const PLACEHOLDER_FIELDS: ReadonlyArray<readonly [string, keyof PlayerInput]> =
|
|
293
292
|
[
|
|
@@ -306,23 +305,8 @@ type PromptIdentity = (roleId: RoleId) => string;
|
|
|
306
305
|
function composePlayerPrompt(
|
|
307
306
|
input: PlayerInput,
|
|
308
307
|
promptIdentity: PromptIdentity,
|
|
308
|
+
resuming = false,
|
|
309
309
|
): string {
|
|
310
|
-
const blocks: string[] = [];
|
|
311
|
-
|
|
312
|
-
if (input.pendingBossQuestion && input.bossReply !== undefined) {
|
|
313
|
-
blocks.push(
|
|
314
|
-
[
|
|
315
|
-
CONTINUATION_PREAMBLE,
|
|
316
|
-
'',
|
|
317
|
-
'Boss question:',
|
|
318
|
-
input.pendingBossQuestion.question,
|
|
319
|
-
'',
|
|
320
|
-
'Boss reply:',
|
|
321
|
-
input.bossReply,
|
|
322
|
-
].join('\n'),
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
310
|
const replacements = new Map<string, string>();
|
|
327
311
|
for (const [placeholder, field] of PLACEHOLDER_FIELDS) {
|
|
328
312
|
const value = input[field];
|
|
@@ -348,8 +332,7 @@ function composePlayerPrompt(
|
|
|
348
332
|
},
|
|
349
333
|
);
|
|
350
334
|
|
|
351
|
-
|
|
352
|
-
return blocks.join('\n\n');
|
|
335
|
+
return composePlayerContinuation(input, body, resuming);
|
|
353
336
|
}
|
|
354
337
|
|
|
355
338
|
// A `result` description names required payload fields in its
|
|
@@ -1518,7 +1501,7 @@ function createDecidePlaybookRuntime(
|
|
|
1518
1501
|
const promptIdentity = (roleId: RoleId): string =>
|
|
1519
1502
|
requireSessionIdentity().roleBindings?.[roleId]?.promptIdentity ?? roleId;
|
|
1520
1503
|
|
|
1521
|
-
const composeInvocationPrompt = (input: PlayerInput): string => {
|
|
1504
|
+
const composeInvocationPrompt = (input: PlayerInput, resuming = false): string => {
|
|
1522
1505
|
let active = true;
|
|
1523
1506
|
const lookup: PromptIdentity = (roleId) => {
|
|
1524
1507
|
if (!active) {
|
|
@@ -1534,7 +1517,7 @@ function createDecidePlaybookRuntime(
|
|
|
1534
1517
|
return promptIdentity(roleId);
|
|
1535
1518
|
};
|
|
1536
1519
|
try {
|
|
1537
|
-
return composePlayerPrompt(input, lookup);
|
|
1520
|
+
return composePlayerPrompt(input, lookup, resuming);
|
|
1538
1521
|
} finally {
|
|
1539
1522
|
active = false;
|
|
1540
1523
|
}
|
|
@@ -2182,7 +2165,7 @@ function createDecidePlaybookRuntime(
|
|
|
2182
2165
|
const roleId = input.role;
|
|
2183
2166
|
const playerId = resolvedPlayerId(roleId);
|
|
2184
2167
|
const playerKey = continuationKey(roleId, playerId);
|
|
2185
|
-
const
|
|
2168
|
+
const freshPrompt = composeInvocationPrompt(input);
|
|
2186
2169
|
let resume: PlayerCallOptions['resume'];
|
|
2187
2170
|
try {
|
|
2188
2171
|
signal.throwIfAborted();
|
|
@@ -2195,6 +2178,7 @@ function createDecidePlaybookRuntime(
|
|
|
2195
2178
|
latchControlPlaneError(error, signal);
|
|
2196
2179
|
throw error;
|
|
2197
2180
|
}
|
|
2181
|
+
const prompt = resume === false ? freshPrompt : composeInvocationPrompt(input, true);
|
|
2198
2182
|
const callId =
|
|
2199
2183
|
continuation?.callId ?? `player-${++playerCallSequence}`;
|
|
2200
2184
|
const identity = {
|
|
@@ -2249,7 +2233,7 @@ function createDecidePlaybookRuntime(
|
|
|
2249
2233
|
try {
|
|
2250
2234
|
signal.throwIfAborted();
|
|
2251
2235
|
const boundary = Promise.resolve(
|
|
2252
|
-
requirePorts().callPlayer(roleId, prompt, signal, { resume }),
|
|
2236
|
+
requirePorts().callPlayer(roleId, prompt, signal, { resume, ...(prompt === freshPrompt ? {} : { freshPrompt }) }),
|
|
2253
2237
|
);
|
|
2254
2238
|
rawResult = await boundary;
|
|
2255
2239
|
// An XState sibling cancellation does not cancel an arbitrary coder
|
|
@@ -5081,7 +5065,6 @@ export const _internal = {
|
|
|
5081
5065
|
VERBATIM_PAYLOAD_FIELDS,
|
|
5082
5066
|
BOSS_INTERRUPT_TARGETS,
|
|
5083
5067
|
UNFINISHED_FINAL_STATE_IDS,
|
|
5084
|
-
CONTINUATION_PREAMBLE,
|
|
5085
5068
|
TELEMETRY_TOPIC,
|
|
5086
5069
|
};
|
|
5087
5070
|
|
package/reference/sdlc/dev.md
CHANGED
|
@@ -15,6 +15,10 @@ It coordinates existing playbooks and owns no repository commit itself.
|
|
|
15
15
|
|
|
16
16
|
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 following instruction:
|
|
17
17
|
|
|
18
|
+
> Original request: <development-request>
|
|
19
|
+
> Prior discussion: <discussion-context>
|
|
20
|
+
> Run results: <run-results>
|
|
21
|
+
|
|
18
22
|
```markdown
|
|
19
23
|
Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.
|
|
20
24
|
Do not change files or commit while planning or discussing the request.
|
|
@@ -23,30 +27,67 @@ Do not change files or commit while planning or discussing the request.
|
|
|
23
27
|
- If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.
|
|
24
28
|
- If implementation can proceed under the existing decisions, choose `code`.
|
|
25
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`.
|
|
26
31
|
|
|
27
32
|
A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.
|
|
28
33
|
Do not choose `decide then code` merely because the work is large.
|
|
29
34
|
Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.
|
|
30
35
|
```
|
|
31
36
|
|
|
32
|
-
The planning result has
|
|
37
|
+
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.
|
|
33
38
|
Each outcome requires affirmative support in Analyst's result; absence of a reason to choose another outcome is not support.
|
|
34
39
|
No outcome depends on a fixed presentation format of Analyst's reply.
|
|
35
40
|
`dev` shall act on the accepted outcome itself and shall not return to the session Captain for another routing decision.
|
|
36
41
|
|
|
37
42
|
For needs Boss reply, `dev` shall use the standard Boss-question suspension with Analyst's complete response.
|
|
38
|
-
The session Captain shall present that response to Boss and, after Boss replies, resume `dev` with the
|
|
43
|
+
The session Captain shall present that response to Boss and, after Boss replies, resume `dev` with the answer in the same Analyst conversation; include the previous question only when that conversation must start fresh.
|
|
39
44
|
|
|
40
45
|
Discussion complete is available only after a Boss reply, when any useful analysis has already been presented through needs Boss reply.
|
|
41
46
|
It completes `dev` without a child call or repository change.
|
|
42
47
|
|
|
43
48
|
For code, `dev` shall directly call playbook `code` with the development request, relevant discussion context, and planning result in quotes (`>`).
|
|
44
49
|
|
|
50
|
+
> Original request: <development-request>
|
|
51
|
+
> Prior discussion: <discussion-context>
|
|
52
|
+
> Planning result: <planning-result>
|
|
53
|
+
|
|
45
54
|
For decide then code, `dev` shall call playbook `decide` with the development request, relevant discussion context, and planning result in quotes (`>`).
|
|
55
|
+
|
|
56
|
+
> Original request: <development-request>
|
|
57
|
+
> Prior discussion: <discussion-context>
|
|
58
|
+
> Planning result: <planning-result>
|
|
59
|
+
|
|
46
60
|
Only after `decide` succeeds shall `dev` call playbook `code` with the development request, relevant discussion context, planning result, `decide`-owned commit, and exact evaluated repository revision in quotes (`>`).
|
|
61
|
+
|
|
62
|
+
> Original request: <development-request>
|
|
63
|
+
> Prior discussion: <discussion-context>
|
|
64
|
+
> Planning result: <planning-result>
|
|
65
|
+
> DECIDE commit: <decide-commit>
|
|
66
|
+
> Evaluated revision: <evaluated-revision>
|
|
67
|
+
|
|
47
68
|
`dev` shall not separately call `review` for the design scope already reviewed by `decide`.
|
|
48
69
|
|
|
70
|
+
For code via pull request and decide then code via pull request, `dev` shall first call playbook `branch` with the development request, relevant discussion context, and planning result in quotes (`>`).
|
|
71
|
+
|
|
72
|
+
> Original request: <development-request>
|
|
73
|
+
> Prior discussion: <discussion-context>
|
|
74
|
+
> Planning result: <planning-result>
|
|
75
|
+
|
|
76
|
+
Only after `branch` succeeds shall `dev` continue 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 above.
|
|
77
|
+
|
|
78
|
+
On a plain path, `code` success completes `dev`.
|
|
79
|
+
On a pull-request path, only after `code` succeeds shall `dev` call playbook `pr` with the development request, issue summary, branch, base revision, last `code`-owned commit, and exact final evaluated repository revision in quotes (`>`).
|
|
80
|
+
|
|
81
|
+
> Original request: <development-request>
|
|
82
|
+
> Issue summary: <issue-summary>
|
|
83
|
+
> Branch: <branch>
|
|
84
|
+
> Base revision: <base-revision>
|
|
85
|
+
> CODE commit: <last-code-commit>
|
|
86
|
+
> Evaluated revision: <final-evaluated-revision>
|
|
87
|
+
|
|
88
|
+
A plain request calls neither `branch` nor `pr`.
|
|
89
|
+
|
|
49
90
|
`dev` completes with the successful result of its final child call.
|
|
50
|
-
If a child returns an authored abort or failure, or a terminal result that does not prove the success required for the selected path, `dev` shall start no later child and shall relay that canonical result.
|
|
91
|
+
If a child returns an authored abort or failure, or a terminal result that does not prove the success required for the selected path, `dev` shall start no later child and shall relay that canonical result; a `branch` or `pr` failure ends `dev` under this same rule.
|
|
51
92
|
If a child call fails outside its authored result contract, `dev` shall park as failed and retain the control-plane error.
|
|
52
|
-
`dev` shall consume commit identities only from each child's canonical structured result, never from player prose.
|
|
93
|
+
`dev` shall consume commit, revision, branch, and pull-request identities only from each child's canonical structured result, never from player prose.
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
2
2
|
readonly [key: string]: JsonValue;
|
|
3
3
|
};
|
|
4
|
-
export type DevStateId = 'planAnalysis' | 'callCode' | 'callDecide' | 'callCodeAfterDecide';
|
|
5
|
-
export type DevSourceItem = 'DEV-1' | 'DEV-2' | 'DEV-3' | 'DEV-4';
|
|
6
|
-
export type DevChildPlaybookId = 'code' | 'decide';
|
|
4
|
+
export type DevStateId = 'planAnalysis' | 'createBranch' | 'callCode' | 'callDecide' | 'callCodeAfterDecide' | 'openPullRequest';
|
|
5
|
+
export type DevSourceItem = 'DEV-1' | 'DEV-2' | 'DEV-3' | 'DEV-4' | 'DEV-5' | 'DEV-6';
|
|
6
|
+
export type DevChildPlaybookId = 'code' | 'decide' | 'branch' | 'pr';
|
|
7
|
+
/**
|
|
8
|
+
* DR-050: the development path a pull-request planning outcome selected. It
|
|
9
|
+
* routes the `branch` success into `callCode` or `callDecide`, so the plain
|
|
10
|
+
* `code` and `decide then code` paths keep their items, prompts, and edges.
|
|
11
|
+
*/
|
|
12
|
+
export type DevPullRequestPath = 'code' | 'decide-then-code';
|
|
7
13
|
export type PendingBossQuestion = {
|
|
8
14
|
readonly questionId: 'planAnalysis';
|
|
9
15
|
readonly resumeStateId: 'planAnalysis';
|
|
@@ -38,13 +44,19 @@ export type PlayerOutput = {
|
|
|
38
44
|
} | {
|
|
39
45
|
readonly guard: 'decideThenCode';
|
|
40
46
|
readonly planningResult: string;
|
|
47
|
+
} | {
|
|
48
|
+
readonly guard: 'codeViaPullRequest';
|
|
49
|
+
readonly planningResult: string;
|
|
50
|
+
} | {
|
|
51
|
+
readonly guard: 'decideThenCodeViaPullRequest';
|
|
52
|
+
readonly planningResult: string;
|
|
41
53
|
} | {
|
|
42
54
|
readonly guard: 'needsBossReply';
|
|
43
55
|
readonly question: string;
|
|
44
56
|
};
|
|
45
57
|
export type PlaybookInput = {
|
|
46
|
-
readonly stateId: 'callCode' | 'callDecide' | 'callCodeAfterDecide';
|
|
47
|
-
readonly sourceItem: 'DEV-2' | 'DEV-3' | 'DEV-4';
|
|
58
|
+
readonly stateId: 'createBranch' | 'callCode' | 'callDecide' | 'callCodeAfterDecide' | 'openPullRequest';
|
|
59
|
+
readonly sourceItem: 'DEV-2' | 'DEV-3' | 'DEV-4' | 'DEV-5' | 'DEV-6';
|
|
48
60
|
readonly playbookId: DevChildPlaybookId;
|
|
49
61
|
readonly text: string;
|
|
50
62
|
};
|
|
@@ -60,6 +72,27 @@ export type DecideSuccessOutput = {
|
|
|
60
72
|
readonly evaluatedRevision: string;
|
|
61
73
|
readonly noUnsettledFindings: true;
|
|
62
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* The fields DEV consumes from `branch`'s canonical structured terminal
|
|
77
|
+
* output before continuing a pull-request path: the exact branch name, the
|
|
78
|
+
* exact base revision, and the issue summary. Additional members belong to
|
|
79
|
+
* BRANCH's own contract and do not disprove success; a missing member is an
|
|
80
|
+
* insufficient result, never an empty default.
|
|
81
|
+
*/
|
|
82
|
+
export type BranchSuccessOutput = {
|
|
83
|
+
readonly branch: string;
|
|
84
|
+
readonly baseRevision: string;
|
|
85
|
+
readonly issueSummary: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The fields DEV consumes from `code`'s canonical structured terminal output
|
|
89
|
+
* before starting the dependent `pr` call: the exact last `code`-owned commit
|
|
90
|
+
* and the exact final evaluated repository revision.
|
|
91
|
+
*/
|
|
92
|
+
export type CodeSuccessOutput = {
|
|
93
|
+
readonly lastCodeCommit: string;
|
|
94
|
+
readonly finalEvaluatedRevision: string;
|
|
95
|
+
};
|
|
63
96
|
export type CompactError = {
|
|
64
97
|
readonly name: string;
|
|
65
98
|
readonly message: string;
|
|
@@ -78,7 +111,8 @@ export type DevPlaybookOutput = {
|
|
|
78
111
|
readonly status: 'discussion-complete';
|
|
79
112
|
} | {
|
|
80
113
|
readonly status: 'complete';
|
|
81
|
-
|
|
114
|
+
/** The final child of the selected path: `pr` on a pull-request path. */
|
|
115
|
+
readonly childPlaybookId: 'code' | 'pr';
|
|
82
116
|
/** The successful result of DEV's final child call, when it has one. */
|
|
83
117
|
readonly childOutput?: JsonValue;
|
|
84
118
|
} | {
|
|
@@ -96,6 +130,14 @@ export type DevContext = {
|
|
|
96
130
|
readonly planningResult?: string;
|
|
97
131
|
readonly decideCommit?: string;
|
|
98
132
|
readonly evaluatedRevision?: string;
|
|
133
|
+
/** DR-050: whether the accepted planning outcome selected a pull-request path. */
|
|
134
|
+
readonly deliveryViaPullRequest: boolean;
|
|
135
|
+
readonly pullRequestPath?: DevPullRequestPath;
|
|
136
|
+
readonly branch?: string;
|
|
137
|
+
readonly baseRevision?: string;
|
|
138
|
+
readonly issueSummary?: string;
|
|
139
|
+
readonly lastCodeCommit?: string;
|
|
140
|
+
readonly finalEvaluatedRevision?: string;
|
|
99
141
|
readonly completion?: 'discussion-complete' | 'complete' | 'child-failed';
|
|
100
142
|
readonly childOutput?: JsonValue;
|
|
101
143
|
readonly childFailure?: CompletedChildResult;
|
|
@@ -164,24 +206,45 @@ export declare const devMachine: import("xstate").StateMachine<DevContext, {
|
|
|
164
206
|
} | {
|
|
165
207
|
type: "rememberDecidePath";
|
|
166
208
|
params: import("xstate").NonReducibleUnknown;
|
|
209
|
+
} | {
|
|
210
|
+
type: "rememberCodeViaPullRequestPath";
|
|
211
|
+
params: import("xstate").NonReducibleUnknown;
|
|
212
|
+
} | {
|
|
213
|
+
type: "rememberDecideThenCodeViaPullRequestPath";
|
|
214
|
+
params: import("xstate").NonReducibleUnknown;
|
|
215
|
+
} | {
|
|
216
|
+
type: "rememberBranchResult";
|
|
217
|
+
params: import("xstate").NonReducibleUnknown;
|
|
167
218
|
} | {
|
|
168
219
|
type: "rememberDecideResult";
|
|
169
220
|
params: import("xstate").NonReducibleUnknown;
|
|
221
|
+
} | {
|
|
222
|
+
type: "rememberCodeResult";
|
|
223
|
+
params: import("xstate").NonReducibleUnknown;
|
|
170
224
|
} | {
|
|
171
225
|
type: "completeWithChildSuccess";
|
|
172
226
|
params: import("xstate").NonReducibleUnknown;
|
|
227
|
+
} | {
|
|
228
|
+
type: "completeWithInsufficientBranchResult";
|
|
229
|
+
params: import("xstate").NonReducibleUnknown;
|
|
173
230
|
} | {
|
|
174
231
|
type: "completeWithInsufficientCodeResult";
|
|
175
232
|
params: import("xstate").NonReducibleUnknown;
|
|
176
233
|
} | {
|
|
177
234
|
type: "completeWithInsufficientDecideResult";
|
|
178
235
|
params: import("xstate").NonReducibleUnknown;
|
|
236
|
+
} | {
|
|
237
|
+
type: "completeWithBranchFailure";
|
|
238
|
+
params: import("xstate").NonReducibleUnknown;
|
|
179
239
|
} | {
|
|
180
240
|
type: "completeWithCodeFailure";
|
|
181
241
|
params: import("xstate").NonReducibleUnknown;
|
|
182
242
|
} | {
|
|
183
243
|
type: "completeWithDecideFailure";
|
|
184
244
|
params: import("xstate").NonReducibleUnknown;
|
|
245
|
+
} | {
|
|
246
|
+
type: "completeWithPrFailure";
|
|
247
|
+
params: import("xstate").NonReducibleUnknown;
|
|
185
248
|
}, {
|
|
186
249
|
type: "needsBossReply";
|
|
187
250
|
params: unknown;
|
|
@@ -198,25 +261,47 @@ export declare const devMachine: import("xstate").StateMachine<DevContext, {
|
|
|
198
261
|
type: "isDecideThenCode";
|
|
199
262
|
params: unknown;
|
|
200
263
|
} | {
|
|
201
|
-
type: "
|
|
264
|
+
type: "isCodeViaPullRequest";
|
|
265
|
+
params: unknown;
|
|
266
|
+
} | {
|
|
267
|
+
type: "isDecideThenCodeViaPullRequest";
|
|
268
|
+
params: unknown;
|
|
269
|
+
} | {
|
|
270
|
+
type: "isBranchSuccessForCode";
|
|
271
|
+
params: unknown;
|
|
272
|
+
} | {
|
|
273
|
+
type: "isBranchSuccessForDecide";
|
|
274
|
+
params: unknown;
|
|
275
|
+
} | {
|
|
276
|
+
type: "isCodeSuccessViaPullRequest";
|
|
277
|
+
params: unknown;
|
|
278
|
+
} | {
|
|
279
|
+
type: "isPlainCodeSuccess";
|
|
202
280
|
params: unknown;
|
|
203
281
|
} | {
|
|
204
282
|
type: "isDecideSuccess";
|
|
205
283
|
params: unknown;
|
|
284
|
+
} | {
|
|
285
|
+
type: "authoredBranchFailure";
|
|
286
|
+
params: unknown;
|
|
206
287
|
} | {
|
|
207
288
|
type: "authoredCodeFailure";
|
|
208
289
|
params: unknown;
|
|
209
290
|
} | {
|
|
210
291
|
type: "authoredDecideFailure";
|
|
211
292
|
params: unknown;
|
|
293
|
+
} | {
|
|
294
|
+
type: "authoredPrFailure";
|
|
295
|
+
params: unknown;
|
|
212
296
|
} | {
|
|
213
297
|
type: "resumesPlanAnalysis";
|
|
214
298
|
params: unknown;
|
|
215
|
-
}, never, "done" | "failed" | "awaitBossReply" | "ready" | "planAnalysis" | "callCode" | "callDecide" | "callCodeAfterDecide" | "discussionComplete" | "reportedChildFailure", string, DevInput, {
|
|
299
|
+
}, never, "done" | "failed" | "awaitBossReply" | "ready" | "planAnalysis" | "createBranch" | "callCode" | "callDecide" | "callCodeAfterDecide" | "openPullRequest" | "discussionComplete" | "reportedChildFailure", string, DevInput, {
|
|
216
300
|
readonly status: "discussion-complete";
|
|
217
301
|
} | {
|
|
218
302
|
readonly status: "complete";
|
|
219
|
-
|
|
303
|
+
/** The final child of the selected path: `pr` on a pull-request path. */
|
|
304
|
+
readonly childPlaybookId: "code" | "pr";
|
|
220
305
|
/** The successful result of DEV's final child call, when it has one. */
|
|
221
306
|
readonly childOutput?: JsonValue;
|
|
222
307
|
} | {
|
|
@@ -232,6 +317,9 @@ export declare const devMachine: import("xstate").StateMachine<DevContext, {
|
|
|
232
317
|
readonly planAnalysis: {
|
|
233
318
|
id: "planAnalysis";
|
|
234
319
|
};
|
|
320
|
+
readonly createBranch: {
|
|
321
|
+
id: "createBranch";
|
|
322
|
+
};
|
|
235
323
|
readonly callCode: {
|
|
236
324
|
id: "callCode";
|
|
237
325
|
};
|
|
@@ -241,6 +329,9 @@ export declare const devMachine: import("xstate").StateMachine<DevContext, {
|
|
|
241
329
|
readonly callCodeAfterDecide: {
|
|
242
330
|
id: "callCodeAfterDecide";
|
|
243
331
|
};
|
|
332
|
+
readonly openPullRequest: {
|
|
333
|
+
id: "openPullRequest";
|
|
334
|
+
};
|
|
244
335
|
readonly awaitBossReply: {
|
|
245
336
|
id: "awaitBossReply";
|
|
246
337
|
};
|