@sublang/playbook 11.0.0 → 12.1.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/docs/cli.md +9 -6
- package/docs/configuration.md +15 -1
- package/docs/embedding.md +83 -1
- package/package.json +28 -3
- package/reference/sdlc/code.playbook/bin/repository-effects.js +501 -170
- package/reference/sdlc/code.playbook/bin/session-store.js +4 -1
- package/reference/sdlc/code.playbook/code.fsm.d.ts +22 -19
- package/reference/sdlc/code.playbook/code.fsm.js +116 -52
- package/reference/sdlc/code.playbook/code.fsm.ts +149 -64
- package/reference/sdlc/code.playbook/code.gears.md +40 -20
- package/reference/sdlc/code.playbook/code.playbook.js +23 -2
- package/reference/sdlc/code.playbook/code.playbook.ts +23 -2
- package/reference/sdlc/code.playbook/host-capabilities.d.ts +291 -0
- package/reference/sdlc/code.playbook/host-capabilities.js +40 -0
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +18 -2
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +13 -6
- package/reference/sdlc/decide.playbook/decide.fsm.js +54 -27
- package/reference/sdlc/decide.playbook/decide.fsm.ts +68 -29
- package/reference/sdlc/decide.playbook/decide.gears.md +25 -19
- package/reference/sdlc/decide.playbook/decide.playbook.js +11 -3
- package/reference/sdlc/decide.playbook/decide.playbook.ts +11 -3
- package/reference/sdlc/decide.playbook/decide.registry.js +1 -1
- package/reference/sdlc/decide.playbook/decide.registry.ts +1 -1
- package/reference/sdlc/dev.md +52 -0
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +261 -0
- package/reference/sdlc/dev.playbook/dev.fsm.js +723 -0
- package/reference/sdlc/dev.playbook/dev.fsm.ts +988 -0
- package/reference/sdlc/dev.playbook/dev.gears.md +91 -0
- package/reference/sdlc/dev.playbook/dev.playbook.d.ts +21 -0
- package/reference/sdlc/dev.playbook/dev.playbook.js +143 -0
- package/reference/sdlc/dev.playbook/dev.playbook.ts +246 -0
- package/reference/sdlc/dev.playbook/dev.registry.d.ts +40 -0
- package/reference/sdlc/dev.playbook/dev.registry.js +64 -0
- package/reference/sdlc/dev.playbook/dev.registry.ts +120 -0
- package/reference/sdlc/review.playbook/review.fsm.d.ts +15 -2
- package/reference/sdlc/review.playbook/review.fsm.js +77 -27
- package/reference/sdlc/review.playbook/review.fsm.ts +96 -30
- package/reference/sdlc/review.playbook/review.gears.md +52 -26
- package/reference/sdlc/review.playbook/review.playbook.js +17 -7
- package/reference/sdlc/review.playbook/review.playbook.ts +17 -7
- package/reference/sdlc/review.playbook/review.registry.js +1 -1
- package/reference/sdlc/review.playbook/review.registry.ts +1 -1
- package/slc/gears2fsm.md +20 -0
- package/slc/link.md +28 -5
- package/slc/text2gears.md +21 -1
- package/src/xstate-playbook-runtime.js +5 -2
- package/src/xstate-playbook-runtime.ts +5 -2
- package/src/xstate-runtime.js +13 -1
- package/src/xstate-runtime.ts +13 -1
|
@@ -43,7 +43,13 @@ type BossReplies = Partial<Record<ResumableStateId, string>>;
|
|
|
43
43
|
type PendingBossQuestionParams = Omit<PendingBossQuestion, 'questionId'>;
|
|
44
44
|
|
|
45
45
|
export interface ReviewSuccessOutput {
|
|
46
|
-
|
|
46
|
+
evaluatedRevision: string;
|
|
47
|
+
noUnsettledFindings: true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DecideSuccessOutput {
|
|
51
|
+
decideCommit: string;
|
|
52
|
+
evaluatedRevision: string;
|
|
47
53
|
noUnsettledFindings: true;
|
|
48
54
|
}
|
|
49
55
|
|
|
@@ -57,7 +63,7 @@ export interface DecideFailureOutput {
|
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
export type DecideOutput =
|
|
66
|
+
export type DecideOutput = DecideSuccessOutput | DecideFailureOutput;
|
|
61
67
|
|
|
62
68
|
interface ChildFailure {
|
|
63
69
|
status: 'aborted' | 'error';
|
|
@@ -73,6 +79,7 @@ export interface DecideContext {
|
|
|
73
79
|
coderProposal?: string;
|
|
74
80
|
reviewerProposal?: string;
|
|
75
81
|
latestCommit?: string;
|
|
82
|
+
coderOutput?: string;
|
|
76
83
|
reviewResult?: ReviewSuccessOutput;
|
|
77
84
|
reviewFailure?: ChildFailure;
|
|
78
85
|
lastResult?: PlayerOutput;
|
|
@@ -105,6 +112,7 @@ export interface PlayerInput {
|
|
|
105
112
|
result: Record<string, string>;
|
|
106
113
|
role: Role;
|
|
107
114
|
callerTopic?: string;
|
|
115
|
+
reviewerProposal?: string;
|
|
108
116
|
pendingBossQuestion?: PendingBossQuestion;
|
|
109
117
|
bossReply?: string;
|
|
110
118
|
}
|
|
@@ -149,37 +157,48 @@ const INDEPENDENT_PROPOSAL_PROMPT = [
|
|
|
149
157
|
'Do not change any files.',
|
|
150
158
|
].join('\n');
|
|
151
159
|
|
|
152
|
-
const
|
|
153
|
-
|
|
160
|
+
const SYNTHESIZE_COMMIT_PROMPT = [
|
|
161
|
+
"Synthesize your independent proposal with Reviewer's proposal below.",
|
|
162
|
+
'Keep to the original topic below and follow what it asks.',
|
|
163
|
+
'Keep the best, essential parts of either proposal and reject any point that is unsound, unnecessary, or outside the topic.',
|
|
164
|
+
'Turn the resulting design into the necessary DRs and/or spec items.',
|
|
154
165
|
'Follow @specs/meta.md and update @specs/map.md when needed.',
|
|
155
|
-
|
|
156
|
-
'Do not change code or implement the proposal.',
|
|
166
|
+
'Do not change code or implement the design.',
|
|
157
167
|
'',
|
|
158
168
|
'Commit the result as one new commit, following @specs/packages/git.md.',
|
|
159
169
|
'Make the commit message explain concisely what changed and why.',
|
|
160
|
-
'
|
|
170
|
+
'Identify every new commit you make.',
|
|
171
|
+
'Coder is <coder-llm> and Reviewer is <reviewer-llm>.',
|
|
172
|
+
'',
|
|
173
|
+
'> Original topic: <caller-topic>',
|
|
174
|
+
"> Reviewer's independent proposal: <reviewer-proposal>",
|
|
161
175
|
].join('\n');
|
|
162
176
|
|
|
163
177
|
const REVIEW_INPUT_TEMPLATE = [
|
|
164
|
-
'
|
|
165
|
-
'
|
|
166
|
-
'
|
|
167
|
-
'',
|
|
168
|
-
'Initial intent: <caller-topic>.',
|
|
169
|
-
"Coder's independent proposal: <coder-proposal>.",
|
|
178
|
+
'> Original intent: <caller-topic>',
|
|
179
|
+
'> Review scope: the `decide`-owned commit <decide-commit> and its resulting repository state.',
|
|
180
|
+
'> Coder output: <coder-output>',
|
|
170
181
|
].join('\n');
|
|
171
182
|
|
|
172
183
|
function composeReviewInput(
|
|
173
184
|
callerTopic: string | undefined,
|
|
174
|
-
|
|
185
|
+
decideCommit: string | undefined,
|
|
186
|
+
coderOutput: string | undefined,
|
|
175
187
|
): string {
|
|
176
188
|
const fields: Readonly<Record<string, string>> = {
|
|
177
189
|
'<caller-topic>': callerTopic ?? '',
|
|
178
|
-
'<
|
|
190
|
+
'<decide-commit>': decideCommit ?? '',
|
|
191
|
+
'<coder-output>': coderOutput ?? '',
|
|
179
192
|
};
|
|
180
193
|
return REVIEW_INPUT_TEMPLATE.replace(
|
|
181
|
-
/<caller-topic>|<coder-
|
|
182
|
-
(placeholder) =>
|
|
194
|
+
/<caller-topic>|<decide-commit>|<coder-output>/g,
|
|
195
|
+
(placeholder, offset: number, source: string) => {
|
|
196
|
+
const value = fields[placeholder];
|
|
197
|
+
const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
|
|
198
|
+
return source.slice(lineStart, offset).startsWith('> ')
|
|
199
|
+
? value.replaceAll('\n', '\n> ')
|
|
200
|
+
: value;
|
|
201
|
+
},
|
|
183
202
|
);
|
|
184
203
|
}
|
|
185
204
|
|
|
@@ -370,20 +389,24 @@ function isPlaybookState(value: unknown): boolean {
|
|
|
370
389
|
);
|
|
371
390
|
}
|
|
372
391
|
|
|
392
|
+
// DECIDE supplied `review` a scope rooted at the `decide`-owned commit, so a
|
|
393
|
+
// result applies to that scope only when it is the exact declared terminal
|
|
394
|
+
// contract: the evaluated repository revision plus the affirmative
|
|
395
|
+
// no-unsettled-findings fact. Any other shape does not establish those facts.
|
|
373
396
|
function reviewSuccessFrom(event: unknown): ReviewSuccessOutput | undefined {
|
|
374
397
|
const output = playbookOutputOf(event);
|
|
375
398
|
if (!isPlainRecord(output)) return undefined;
|
|
376
|
-
if (!isExactKeys(output, ['
|
|
399
|
+
if (!isExactKeys(output, ['evaluatedRevision', 'noUnsettledFindings'])) {
|
|
377
400
|
return undefined;
|
|
378
401
|
}
|
|
379
402
|
if (
|
|
380
|
-
output.
|
|
403
|
+
!isNonEmptyString(output.evaluatedRevision) ||
|
|
381
404
|
output.noUnsettledFindings !== true
|
|
382
405
|
) {
|
|
383
406
|
return undefined;
|
|
384
407
|
}
|
|
385
408
|
return {
|
|
386
|
-
|
|
409
|
+
evaluatedRevision: output.evaluatedRevision,
|
|
387
410
|
noUnsettledFindings: true,
|
|
388
411
|
};
|
|
389
412
|
}
|
|
@@ -537,6 +560,7 @@ export const decideMachine = setup({
|
|
|
537
560
|
coderProposal: undefined,
|
|
538
561
|
reviewerProposal: undefined,
|
|
539
562
|
latestCommit: undefined,
|
|
563
|
+
coderOutput: undefined,
|
|
540
564
|
reviewResult: undefined,
|
|
541
565
|
reviewFailure: undefined,
|
|
542
566
|
lastResult: undefined,
|
|
@@ -552,6 +576,7 @@ export const decideMachine = setup({
|
|
|
552
576
|
coderProposal: undefined,
|
|
553
577
|
reviewerProposal: undefined,
|
|
554
578
|
latestCommit: undefined,
|
|
579
|
+
coderOutput: undefined,
|
|
555
580
|
reviewResult: undefined,
|
|
556
581
|
reviewFailure: undefined,
|
|
557
582
|
lastResult: undefined,
|
|
@@ -583,6 +608,7 @@ export const decideMachine = setup({
|
|
|
583
608
|
}),
|
|
584
609
|
rememberCommit: assign({
|
|
585
610
|
latestCommit: ({ event }) => outputOf(event).latestCommit,
|
|
611
|
+
coderOutput: ({ event }) => outputOf(event).coderOutput,
|
|
586
612
|
lastResult: ({ event }) => outputOf(event),
|
|
587
613
|
lastError: undefined,
|
|
588
614
|
}),
|
|
@@ -603,7 +629,7 @@ export const decideMachine = setup({
|
|
|
603
629
|
error: {
|
|
604
630
|
name: 'ReviewProtocolError',
|
|
605
631
|
message:
|
|
606
|
-
'REVIEW returned without
|
|
632
|
+
'REVIEW returned without the evaluated repository revision and a no-unsettled-findings result for the supplied scope.',
|
|
607
633
|
},
|
|
608
634
|
},
|
|
609
635
|
lastError: undefined,
|
|
@@ -678,7 +704,16 @@ export const decideMachine = setup({
|
|
|
678
704
|
initial: 'ready',
|
|
679
705
|
context: (): DecideContext => ({}),
|
|
680
706
|
output: ({ context }): DecideOutput => {
|
|
681
|
-
if (context.reviewResult)
|
|
707
|
+
if (context.reviewResult) {
|
|
708
|
+
if (!context.latestCommit) {
|
|
709
|
+
throw new Error('DECIDE completed without its decide-owned commit');
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
decideCommit: context.latestCommit,
|
|
713
|
+
evaluatedRevision: context.reviewResult.evaluatedRevision,
|
|
714
|
+
noUnsettledFindings: true,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
682
717
|
if (!context.latestCommit || !context.reviewFailure) {
|
|
683
718
|
throw new Error('DECIDE reached a final state without a result');
|
|
684
719
|
}
|
|
@@ -747,7 +782,7 @@ export const decideMachine = setup({
|
|
|
747
782
|
prompt: INDEPENDENT_PROPOSAL_PROMPT,
|
|
748
783
|
result: withNeedsBossReply({
|
|
749
784
|
proposed:
|
|
750
|
-
'Coder
|
|
785
|
+
'Coder affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `coderProposal: <verbatim final text>`.',
|
|
751
786
|
}),
|
|
752
787
|
callerTopic: context.callerTopic,
|
|
753
788
|
...bossReplyFields(context, 'askCoderProposal'),
|
|
@@ -887,7 +922,7 @@ export const decideMachine = setup({
|
|
|
887
922
|
prompt: INDEPENDENT_PROPOSAL_PROMPT,
|
|
888
923
|
result: withNeedsBossReply({
|
|
889
924
|
proposed:
|
|
890
|
-
'Reviewer
|
|
925
|
+
'Reviewer affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `reviewerProposal: <verbatim final text>`.',
|
|
891
926
|
}),
|
|
892
927
|
callerTopic: context.callerTopic,
|
|
893
928
|
...bossReplyFields(context, 'askReviewerProposal'),
|
|
@@ -1014,11 +1049,12 @@ export const decideMachine = setup({
|
|
|
1014
1049
|
commitCoderProposal: {
|
|
1015
1050
|
id: 'commitCoderProposal',
|
|
1016
1051
|
tags: 'playbook.busy',
|
|
1017
|
-
description: 'Coder
|
|
1052
|
+
description: 'Coder synthesizes both proposals and commits the design.',
|
|
1018
1053
|
meta: {
|
|
1019
1054
|
playbook: {
|
|
1020
1055
|
stateId: 'commitCoderProposal',
|
|
1021
|
-
description:
|
|
1056
|
+
description:
|
|
1057
|
+
'Coder synthesizes both proposals and commits the design.',
|
|
1022
1058
|
role: 'coder',
|
|
1023
1059
|
},
|
|
1024
1060
|
},
|
|
@@ -1028,11 +1064,13 @@ export const decideMachine = setup({
|
|
|
1028
1064
|
stateId: 'commitCoderProposal',
|
|
1029
1065
|
sourceItem: 'DECIDE-3',
|
|
1030
1066
|
role: 'coder',
|
|
1031
|
-
prompt:
|
|
1067
|
+
prompt: SYNTHESIZE_COMMIT_PROMPT,
|
|
1032
1068
|
result: withNeedsBossReply({
|
|
1033
1069
|
committed:
|
|
1034
|
-
|
|
1070
|
+
'Coder synthesized both proposals and committed the resulting design as one new commit. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.',
|
|
1035
1071
|
}),
|
|
1072
|
+
callerTopic: context.callerTopic,
|
|
1073
|
+
reviewerProposal: context.reviewerProposal,
|
|
1036
1074
|
...bossReplyFields(context, 'commitCoderProposal'),
|
|
1037
1075
|
}),
|
|
1038
1076
|
onDone: [
|
|
@@ -1141,7 +1179,8 @@ export const decideMachine = setup({
|
|
|
1141
1179
|
playbookId: 'review',
|
|
1142
1180
|
text: composeReviewInput(
|
|
1143
1181
|
context.callerTopic,
|
|
1144
|
-
context.
|
|
1182
|
+
context.latestCommit,
|
|
1183
|
+
context.coderOutput,
|
|
1145
1184
|
),
|
|
1146
1185
|
}),
|
|
1147
1186
|
onDone: [
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
The caller supplies a topic as `callerTopic`.
|
|
14
14
|
Coder and Reviewer receive the complete topic concurrently and independently.
|
|
15
|
-
Neither role's player receives the other role's proposal before both proposals
|
|
15
|
+
Neither role's player receives the other role's proposal before both proposals are complete.
|
|
16
|
+
A proposal is complete only when its player affirmatively provides a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome.
|
|
16
17
|
A Boss interrupt during the parallel proposal pair restarts the complete pair with the new `callerTopic`.
|
|
17
18
|
|
|
18
19
|
### DECIDE-1
|
|
@@ -30,7 +31,7 @@ When the caller gives a topic, Captain shall relay the complete topic to Coder i
|
|
|
30
31
|
> Do not change any files.
|
|
31
32
|
|
|
32
33
|
Results:
|
|
33
|
-
- `proposed`: Coder
|
|
34
|
+
- `proposed`: Coder affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `coderProposal: <verbatim final text>`.
|
|
34
35
|
|
|
35
36
|
### DECIDE-2
|
|
36
37
|
|
|
@@ -47,39 +48,44 @@ When the caller gives a topic, Captain shall relay the complete topic to Reviewe
|
|
|
47
48
|
> Do not change any files.
|
|
48
49
|
|
|
49
50
|
Results:
|
|
50
|
-
- `proposed`: Reviewer
|
|
51
|
+
- `proposed`: Reviewer affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `reviewerProposal: <verbatim final text>`.
|
|
51
52
|
|
|
52
|
-
##
|
|
53
|
+
## Synthesize and commit
|
|
53
54
|
|
|
54
55
|
### DECIDE-3
|
|
55
56
|
|
|
56
|
-
When both independent proposals are complete, Captain shall prompt Coder:
|
|
57
|
+
When both independent proposals are complete, Captain shall relay the complete topic and Reviewer's complete proposal to Coder under their own labels in quotes and prompt Coder:
|
|
57
58
|
|
|
58
|
-
>
|
|
59
|
+
> Synthesize your independent proposal with Reviewer's proposal below.
|
|
60
|
+
> Keep to the original topic below and follow what it asks.
|
|
61
|
+
> Keep the best, essential parts of either proposal and reject any point that is unsound, unnecessary, or outside the topic.
|
|
62
|
+
> Turn the resulting design into the necessary DRs and/or spec items.
|
|
59
63
|
> Follow @specs/meta.md and update @specs/map.md when needed.
|
|
60
|
-
> Do not
|
|
61
|
-
> Do not change code or implement the proposal.
|
|
64
|
+
> Do not change code or implement the design.
|
|
62
65
|
>
|
|
63
66
|
> Commit the result as one new commit, following @specs/packages/git.md.
|
|
64
67
|
> Make the commit message explain concisely what changed and why.
|
|
65
|
-
>
|
|
68
|
+
> Identify every new commit you make.
|
|
69
|
+
> Coder is <coder-llm> and Reviewer is <reviewer-llm>.
|
|
70
|
+
>
|
|
71
|
+
> > Original topic: <caller-topic>
|
|
72
|
+
> > Reviewer's independent proposal: <reviewer-proposal>
|
|
66
73
|
|
|
67
74
|
Results:
|
|
68
|
-
- `committed`: Coder committed
|
|
75
|
+
- `committed`: Coder synthesized both proposals and committed the resulting design as one new commit. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.
|
|
76
|
+
|
|
77
|
+
No transition depends on a fixed presentation format of either player's reply; the repository-effect receipt is the authoritative identity of Coder's new `decide`-owned commit.
|
|
69
78
|
|
|
70
79
|
## Review
|
|
71
80
|
|
|
72
81
|
### DECIDE-4
|
|
73
82
|
|
|
74
|
-
When Coder commits, Captain shall call playbook `review
|
|
83
|
+
When Coder commits, Captain shall call playbook `review` with the following input in quotes:
|
|
75
84
|
|
|
76
|
-
>
|
|
77
|
-
>
|
|
78
|
-
>
|
|
79
|
-
>
|
|
80
|
-
> Initial intent: <caller-topic>.
|
|
81
|
-
> Coder's independent proposal: <coder-proposal>.
|
|
85
|
+
> > Original intent: <caller-topic>
|
|
86
|
+
> > Review scope: the `decide`-owned commit <decide-commit> and its resulting repository state.
|
|
87
|
+
> > Coder output: <coder-output>
|
|
82
88
|
|
|
83
|
-
|
|
84
|
-
An authored
|
|
89
|
+
`decide` is complete only when `review` returns a result that applies to the supplied review scope, gives the exact evaluated repository revision, and affirmatively establishes that no unsettled findings remain; `decide` then returns the `decide`-owned commit and that evaluated revision to the caller.
|
|
90
|
+
An authored `review` abort or failure, or a terminal result that does not establish those facts, terminates with the failure and the last `decide`-owned commit reported to the caller.
|
|
85
91
|
Any other nested-call error parks `decide` as failed and retains the control-plane error.
|
|
@@ -123,7 +123,10 @@ const TRACE_TOPIC = 'playbook.trace';
|
|
|
123
123
|
const UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID = 'reconcile:unresolved-effect';
|
|
124
124
|
const UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID = 'abandon:unresolved-effect';
|
|
125
125
|
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.';
|
|
126
|
-
const PLACEHOLDER_FIELDS = [
|
|
126
|
+
const PLACEHOLDER_FIELDS = [
|
|
127
|
+
['<caller-topic>', 'callerTopic'],
|
|
128
|
+
['<reviewer-proposal>', 'reviewerProposal'],
|
|
129
|
+
];
|
|
127
130
|
const VERBATIM_PAYLOAD_FIELDS = new Set([
|
|
128
131
|
'coderProposal',
|
|
129
132
|
'reviewerProposal',
|
|
@@ -151,12 +154,17 @@ function composePlayerPrompt(input, promptIdentity) {
|
|
|
151
154
|
if (input.prompt.includes('<coder-llm>')) {
|
|
152
155
|
replacements.set('<coder-llm>', promptIdentity('coder'));
|
|
153
156
|
}
|
|
154
|
-
|
|
157
|
+
if (input.prompt.includes('<reviewer-llm>')) {
|
|
158
|
+
replacements.set('<reviewer-llm>', promptIdentity('reviewer'));
|
|
159
|
+
}
|
|
160
|
+
const body = input.prompt.replace(/<caller-topic>|<reviewer-proposal>|<coder-llm>|<reviewer-llm>/g, (placeholder, offset, source) => {
|
|
155
161
|
const value = replacements.get(placeholder);
|
|
156
162
|
if (value === undefined)
|
|
157
163
|
return placeholder;
|
|
164
|
+
// A labelled quoted relay keeps a multiline value inside its authored
|
|
165
|
+
// blockquote, so continuation lines re-enter the quote marker.
|
|
158
166
|
const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
|
|
159
|
-
return source.slice(lineStart, offset)
|
|
167
|
+
return source.slice(lineStart, offset).startsWith('> ')
|
|
160
168
|
? value.replaceAll('\n', '\n> ')
|
|
161
169
|
: value;
|
|
162
170
|
});
|
|
@@ -284,7 +284,10 @@ const CONTINUATION_PREAMBLE =
|
|
|
284
284
|
'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
|
|
285
285
|
|
|
286
286
|
const PLACEHOLDER_FIELDS: ReadonlyArray<readonly [string, keyof PlayerInput]> =
|
|
287
|
-
[
|
|
287
|
+
[
|
|
288
|
+
['<caller-topic>', 'callerTopic'],
|
|
289
|
+
['<reviewer-proposal>', 'reviewerProposal'],
|
|
290
|
+
];
|
|
288
291
|
|
|
289
292
|
const VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string> = new Set([
|
|
290
293
|
'coderProposal',
|
|
@@ -322,13 +325,18 @@ function composePlayerPrompt(
|
|
|
322
325
|
if (input.prompt.includes('<coder-llm>')) {
|
|
323
326
|
replacements.set('<coder-llm>', promptIdentity('coder'));
|
|
324
327
|
}
|
|
328
|
+
if (input.prompt.includes('<reviewer-llm>')) {
|
|
329
|
+
replacements.set('<reviewer-llm>', promptIdentity('reviewer'));
|
|
330
|
+
}
|
|
325
331
|
const body = input.prompt.replace(
|
|
326
|
-
/<caller-topic>|<coder-llm>/g,
|
|
332
|
+
/<caller-topic>|<reviewer-proposal>|<coder-llm>|<reviewer-llm>/g,
|
|
327
333
|
(placeholder, offset: number, source: string) => {
|
|
328
334
|
const value = replacements.get(placeholder);
|
|
329
335
|
if (value === undefined) return placeholder;
|
|
336
|
+
// A labelled quoted relay keeps a multiline value inside its authored
|
|
337
|
+
// blockquote, so continuation lines re-enter the quote marker.
|
|
330
338
|
const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
|
|
331
|
-
return source.slice(lineStart, offset)
|
|
339
|
+
return source.slice(lineStart, offset).startsWith('> ')
|
|
332
340
|
? value.replaceAll('\n', '\n> ')
|
|
333
341
|
: value;
|
|
334
342
|
},
|
|
@@ -41,7 +41,7 @@ export function validateDecideOptions(optionSlice) {
|
|
|
41
41
|
export const decidePlaybookRegistryEntry = {
|
|
42
42
|
id: 'decide',
|
|
43
43
|
command: 'decide',
|
|
44
|
-
intent: '
|
|
44
|
+
intent: 'synthesize independent Coder and Reviewer proposals into an approved spec-design commit',
|
|
45
45
|
artifactSchema: 3,
|
|
46
46
|
runtimeProfile: Object.freeze({
|
|
47
47
|
kind: 'bespoke',
|
|
@@ -95,7 +95,7 @@ export const decidePlaybookRegistryEntry: DecidePlaybookRegistryEntry = {
|
|
|
95
95
|
id: 'decide',
|
|
96
96
|
command: 'decide',
|
|
97
97
|
intent:
|
|
98
|
-
'
|
|
98
|
+
'synthesize independent Coder and Reviewer proposals into an approved spec-design commit',
|
|
99
99
|
artifactSchema: 3,
|
|
100
100
|
runtimeProfile: Object.freeze({
|
|
101
101
|
kind: 'bespoke',
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
# Dev
|
|
6
|
+
|
|
7
|
+
Roles:
|
|
8
|
+
|
|
9
|
+
- Analyst
|
|
10
|
+
|
|
11
|
+
The caller supplies a development request including any desired outcome, scope, and context.
|
|
12
|
+
|
|
13
|
+
`dev` is an optional repository-aware planner for a development request that needs more analysis before choosing a development path.
|
|
14
|
+
It coordinates existing playbooks and owns no repository commit itself.
|
|
15
|
+
|
|
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
|
+
|
|
18
|
+
```markdown
|
|
19
|
+
Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.
|
|
20
|
+
Do not change files or commit while planning or discussing the request.
|
|
21
|
+
|
|
22
|
+
- If useful analysis or clarification should be discussed before any repository work, give Boss the useful response and ask one material question that advances the decision.
|
|
23
|
+
- If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.
|
|
24
|
+
- If implementation can proceed under the existing decisions, choose `code`.
|
|
25
|
+
- If implementation first requires a new or amended durable decision that the existing specs do not settle, choose `decide then code`.
|
|
26
|
+
|
|
27
|
+
A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.
|
|
28
|
+
Do not choose `decide then code` merely because the work is large.
|
|
29
|
+
Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The planning result has four semantic outcomes: needs Boss reply, discussion complete, code, and decide then code.
|
|
33
|
+
Each outcome requires affirmative support in Analyst's result; absence of a reason to choose another outcome is not support.
|
|
34
|
+
No outcome depends on a fixed presentation format of Analyst's reply.
|
|
35
|
+
`dev` shall act on the accepted outcome itself and shall not return to the session Captain for another routing decision.
|
|
36
|
+
|
|
37
|
+
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 question and answer in the same Analyst conversation.
|
|
39
|
+
|
|
40
|
+
Discussion complete is available only after a Boss reply, when any useful analysis has already been presented through needs Boss reply.
|
|
41
|
+
It completes `dev` without a child call or repository change.
|
|
42
|
+
|
|
43
|
+
For code, `dev` shall directly call playbook `code` with the development request, relevant discussion context, and planning result in quotes (`>`).
|
|
44
|
+
|
|
45
|
+
For decide then code, `dev` shall call playbook `decide` with the development request, relevant discussion context, and planning result in quotes (`>`).
|
|
46
|
+
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 (`>`).
|
|
47
|
+
`dev` shall not separately call `review` for the design scope already reviewed by `decide`.
|
|
48
|
+
|
|
49
|
+
`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.
|
|
51
|
+
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.
|