@sublang/playbook 7.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/docs/cli.md +74 -29
- package/docs/configuration.md +209 -112
- package/docs/embedding.md +71 -25
- package/package.json +4 -3
- package/reference/sdlc/captain.playbook/captain.playbook.js +3 -3
- package/reference/sdlc/captain.playbook/captain.playbook.ts +3 -3
- package/reference/sdlc/code.md +1 -1
- package/reference/sdlc/code.playbook/bin/interactive-session.js +816 -0
- package/reference/sdlc/code.playbook/bin/launch-config.js +1078 -116
- package/reference/sdlc/code.playbook/bin/playbook.js +489 -34
- package/reference/sdlc/code.playbook/bin/run.js +283 -298
- package/reference/sdlc/code.playbook/bin/session-store.js +818 -26
- package/reference/sdlc/code.playbook/code.fsm.d.ts +5 -5
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +2 -2
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +2 -2
- package/reference/sdlc/code.playbook/code.fsm.js +7 -11
- package/reference/sdlc/code.playbook/code.fsm.ts +9 -17
- package/reference/sdlc/code.playbook/code.gears.md +1 -1
- package/reference/sdlc/code.playbook/code.playbook.d.ts +2 -1
- package/reference/sdlc/code.playbook/code.playbook.js +12 -13
- package/reference/sdlc/code.playbook/code.playbook.ts +22 -15
- package/reference/sdlc/code.playbook/code.registry.d.ts +5 -13
- package/reference/sdlc/code.playbook/code.registry.js +3 -10
- package/reference/sdlc/code.playbook/code.registry.ts +7 -32
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +39 -14
- package/reference/sdlc/code.playbook/playbook-captain.js +970 -289
- package/reference/sdlc/code.playbook/playbook-captain.ts +1403 -396
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +41 -49
- package/reference/sdlc/decide.md +4 -4
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +9 -9
- package/reference/sdlc/decide.playbook/decide.fsm.js +21 -14
- package/reference/sdlc/decide.playbook/decide.fsm.ts +27 -23
- package/reference/sdlc/decide.playbook/decide.gears.md +3 -5
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +9 -13
- package/reference/sdlc/decide.playbook/decide.playbook.js +171 -134
- package/reference/sdlc/decide.playbook/decide.playbook.ts +238 -162
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +5 -13
- package/reference/sdlc/decide.playbook/decide.registry.js +3 -9
- package/reference/sdlc/decide.playbook/decide.registry.ts +7 -31
- package/reference/sdlc/review.md +4 -5
- package/reference/sdlc/review.playbook/review.fsm.d.ts +9 -11
- package/reference/sdlc/review.playbook/review.fsm.js +30 -24
- package/reference/sdlc/review.playbook/review.fsm.ts +39 -35
- package/reference/sdlc/review.playbook/review.gears.md +6 -5
- package/reference/sdlc/review.playbook/review.playbook.d.ts +2 -1
- package/reference/sdlc/review.playbook/review.playbook.js +16 -21
- package/reference/sdlc/review.playbook/review.playbook.ts +26 -26
- package/reference/sdlc/review.playbook/review.registry.d.ts +5 -13
- package/reference/sdlc/review.playbook/review.registry.js +3 -16
- package/reference/sdlc/review.playbook/review.registry.ts +7 -38
- package/slc/gears2fsm.md +27 -23
- package/slc/link.md +113 -93
- package/slc/text2gears.md +19 -18
- package/src/runtime.d.ts +20 -16
- package/src/runtime.ts +19 -23
- package/src/xstate-playbook-runtime.d.ts +21 -17
- package/src/xstate-playbook-runtime.js +241 -149
- package/src/xstate-playbook-runtime.ts +331 -178
- package/src/xstate-runtime.js +63 -24
- package/src/xstate-runtime.ts +96 -28
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
# SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
|
|
4
4
|
# Shared `playbook` and `playbook run` launcher config.
|
|
5
|
-
# Top-level host fields only — no `config:` wrapper
|
|
6
|
-
#
|
|
7
|
-
#
|
|
5
|
+
# Top-level host fields only — no `config:` wrapper. The launcher injects
|
|
6
|
+
# captain.from, retains only players named by a role binding, and hosts the
|
|
7
|
+
# same Playbook Captain shell interactively or headlessly.
|
|
8
8
|
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# adapter/model/effort/permissions.
|
|
12
|
-
#
|
|
9
|
+
# The Captain and each stable session player carries its own defaults inline:
|
|
10
|
+
# an adapter shorthand (claude, codex) or a block with
|
|
11
|
+
# adapter/model/effort/permissions. A role may override only model or effort.
|
|
12
|
+
# Omit an override to inherit the player default; set it to false to select
|
|
13
|
+
# that provider's current default explicitly.
|
|
13
14
|
|
|
14
15
|
# Each adapter needs its vendor SDK installed as its own top-level
|
|
15
16
|
# install root — they are optional peer dependencies, so you pay only
|
|
@@ -23,9 +24,9 @@
|
|
|
23
24
|
# so they can inspect and work with repository metadata under the sandbox.
|
|
24
25
|
|
|
25
26
|
# The Captain/Judge agent.
|
|
26
|
-
# Captain routing/adjudication calls run tool-free. Claude
|
|
27
|
-
# the provider level;
|
|
28
|
-
#
|
|
27
|
+
# Captain routing/adjudication calls run tool-free. Claude and Gemini enforce
|
|
28
|
+
# that at the provider level; codex, kimi, and opencode cannot enforce a tool
|
|
29
|
+
# list, so those captains degrade to a prompt-level restriction (DR-013 A1).
|
|
29
30
|
captain:
|
|
30
31
|
adapter: claude
|
|
31
32
|
model: claude-opus-4-8
|
|
@@ -39,55 +40,46 @@ notifications:
|
|
|
39
40
|
player_finished: bell
|
|
40
41
|
turn_finished: desktop
|
|
41
42
|
|
|
43
|
+
# Stable player ids are flat exact strings. Dots are a naming convention, not
|
|
44
|
+
# YAML hierarchy. Naming the same id in several role bindings deliberately
|
|
45
|
+
# shares one provider conversation across those playbooks.
|
|
46
|
+
players:
|
|
47
|
+
dev.coder:
|
|
48
|
+
adapter: claude
|
|
49
|
+
model: claude-opus-4-8[1m]
|
|
50
|
+
effort: xhigh
|
|
51
|
+
permissions:
|
|
52
|
+
mode: auto
|
|
53
|
+
|
|
54
|
+
dev.reviewer:
|
|
55
|
+
adapter: codex
|
|
56
|
+
model: gpt-5.5
|
|
57
|
+
effort: xhigh
|
|
58
|
+
permissions:
|
|
59
|
+
mode: auto
|
|
60
|
+
writablePaths: ['.git']
|
|
61
|
+
|
|
42
62
|
# Enabled playbooks. Each is loaded from its explicit `from` module; the
|
|
43
63
|
# `<id>` key must equal that module's manifest id. `from`, `command`, and
|
|
44
|
-
# `
|
|
64
|
+
# `roles` are launcher-owned; every other key is that playbook's option
|
|
45
65
|
# slice.
|
|
46
66
|
#
|
|
47
|
-
#
|
|
48
|
-
# same
|
|
49
|
-
# ancestor sessions. A standalone REVIEW uses its own bindings below.
|
|
67
|
+
# Every manifest role is bound explicitly. These three playbooks intentionally
|
|
68
|
+
# reuse the same two stable players; change an id to isolate its conversation.
|
|
50
69
|
playbooks:
|
|
51
70
|
code:
|
|
52
71
|
from: "@sublang/playbook/code/registry"
|
|
53
|
-
|
|
54
|
-
coder:
|
|
55
|
-
adapter: claude
|
|
56
|
-
model: claude-opus-4-8[1m]
|
|
57
|
-
effort: xhigh
|
|
58
|
-
permissions:
|
|
59
|
-
mode: auto
|
|
72
|
+
roles:
|
|
73
|
+
coder: dev.coder
|
|
60
74
|
|
|
61
75
|
review:
|
|
62
76
|
from: "@sublang/playbook/review/registry"
|
|
63
|
-
|
|
64
|
-
coder:
|
|
65
|
-
|
|
66
|
-
model: claude-opus-4-8[1m]
|
|
67
|
-
effort: xhigh
|
|
68
|
-
permissions:
|
|
69
|
-
mode: auto
|
|
70
|
-
reviewer:
|
|
71
|
-
adapter: codex
|
|
72
|
-
model: gpt-5.5
|
|
73
|
-
effort: xhigh
|
|
74
|
-
permissions:
|
|
75
|
-
mode: auto
|
|
76
|
-
writablePaths: ['.git']
|
|
77
|
+
roles:
|
|
78
|
+
coder: dev.coder
|
|
79
|
+
reviewer: dev.reviewer
|
|
77
80
|
|
|
78
81
|
decide:
|
|
79
82
|
from: "@sublang/playbook/decide/registry"
|
|
80
|
-
|
|
81
|
-
coder:
|
|
82
|
-
|
|
83
|
-
model: claude-opus-4-8[1m]
|
|
84
|
-
effort: xhigh
|
|
85
|
-
permissions:
|
|
86
|
-
mode: auto
|
|
87
|
-
reviewer:
|
|
88
|
-
adapter: codex
|
|
89
|
-
model: gpt-5.5
|
|
90
|
-
effort: xhigh
|
|
91
|
-
permissions:
|
|
92
|
-
mode: auto
|
|
93
|
-
writablePaths: ['.git']
|
|
83
|
+
roles:
|
|
84
|
+
coder: dev.coder
|
|
85
|
+
reviewer: dev.reviewer
|
package/reference/sdlc/decide.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Decide
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Roles:
|
|
8
8
|
|
|
9
9
|
- Coder
|
|
10
10
|
- Reviewer
|
|
@@ -13,7 +13,7 @@ The caller supplies the topic including any specific context.
|
|
|
13
13
|
|
|
14
14
|
When the caller gives a topic, Captain shall relay the complete topic in quotes (`>`) to Coder and Reviewer concurrently and independently.
|
|
15
15
|
Captain shall not wait for either proposal before requesting the other.
|
|
16
|
-
Captain shall give
|
|
16
|
+
Captain shall give the player bound to each role the following instruction:
|
|
17
17
|
|
|
18
18
|
```markdown
|
|
19
19
|
Assess whether the topic is better expressed as a few spec items under @specs/packages/ or requires one or more DRs under @specs/decisions/.
|
|
@@ -23,7 +23,7 @@ Consult @specs/map.md for relevant context and @specs/meta.md for spec requireme
|
|
|
23
23
|
Do not change any files.
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Neither player shall receive the other's proposal until both proposals are complete and Coder has committed Coder's own proposal.
|
|
26
|
+
Neither role's player shall receive the other role's proposal until both proposals are complete and Coder has committed Coder's own proposal.
|
|
27
27
|
A Boss interrupt during the parallel proposal pair shall restart the whole pair so both players receive the same new topic and remain independent.
|
|
28
28
|
|
|
29
29
|
When both proposals are complete, Captain shall then give Coder the following instruction:
|
|
@@ -36,7 +36,7 @@ Do not change code or implement the proposal.
|
|
|
36
36
|
|
|
37
37
|
Commit the result as one new commit, following @specs/packages/git.md.
|
|
38
38
|
Make the commit message explain concisely what changed and why.
|
|
39
|
-
|
|
39
|
+
Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.
|
|
40
40
|
Coder is <coder-llm>; format the model token in conventional human form.
|
|
41
41
|
```
|
|
42
42
|
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
2
2
|
readonly [key: string]: JsonValue;
|
|
3
3
|
};
|
|
4
|
-
type
|
|
4
|
+
type Role = 'coder' | 'reviewer';
|
|
5
|
+
export declare const concurrentRoleSets: readonly [readonly ["coder", "reviewer"]];
|
|
5
6
|
type JumpableStateId = 'independentProposals';
|
|
6
7
|
type ResumableStateId = 'askCoderProposal' | 'askReviewerProposal' | 'commitCoderProposal';
|
|
7
8
|
export interface PendingBossQuestion {
|
|
8
9
|
questionId: ResumableStateId;
|
|
9
10
|
resumeStateId: ResumableStateId;
|
|
10
11
|
sourceItem: string;
|
|
11
|
-
|
|
12
|
+
asker: {
|
|
13
|
+
kind: 'role';
|
|
14
|
+
roleId: Role;
|
|
15
|
+
};
|
|
12
16
|
question: string;
|
|
13
17
|
}
|
|
14
18
|
type PendingBossQuestions = Partial<Record<ResumableStateId, PendingBossQuestion>>;
|
|
@@ -37,7 +41,6 @@ interface ChildFailure {
|
|
|
37
41
|
};
|
|
38
42
|
}
|
|
39
43
|
export interface DecideContext {
|
|
40
|
-
coderLlm: string;
|
|
41
44
|
callerTopic?: string;
|
|
42
45
|
coderProposal?: string;
|
|
43
46
|
reviewerProposal?: string;
|
|
@@ -63,17 +66,14 @@ export type DecideEvent = {
|
|
|
63
66
|
questionId?: ResumableStateId;
|
|
64
67
|
answer: string;
|
|
65
68
|
};
|
|
66
|
-
export
|
|
67
|
-
coderLlm: string;
|
|
68
|
-
}
|
|
69
|
+
export type DecideInput = Readonly<Record<string, never>>;
|
|
69
70
|
export interface PlayerInput {
|
|
70
71
|
stateId: ResumableStateId;
|
|
71
72
|
sourceItem: string;
|
|
72
73
|
prompt: string;
|
|
73
74
|
result: Record<string, string>;
|
|
74
|
-
|
|
75
|
+
role: Role;
|
|
75
76
|
callerTopic?: string;
|
|
76
|
-
coderLlm?: string;
|
|
77
77
|
pendingBossQuestion?: PendingBossQuestion;
|
|
78
78
|
bossReply?: string;
|
|
79
79
|
}
|
|
@@ -205,7 +205,7 @@ export declare const decideMachine: import("xstate").StateMachine<DecideContext,
|
|
|
205
205
|
coder: "complete" | "working" | "waiting";
|
|
206
206
|
reviewer: "complete" | "working" | "waiting";
|
|
207
207
|
};
|
|
208
|
-
}, string,
|
|
208
|
+
}, string, Readonly<Record<string, never>>, ReviewSuccessOutput | DecideFailureOutput, import("xstate").EventObject, import("xstate").MetaObject, {
|
|
209
209
|
id: "decide";
|
|
210
210
|
states: {
|
|
211
211
|
readonly ready: {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// This module defines only the machine, actor contracts, and typed inputs.
|
|
5
5
|
// The linked runtime supplies the player and nested-playbook actors.
|
|
6
6
|
import { assign, fromPromise, setup } from 'xstate';
|
|
7
|
+
export const concurrentRoleSets = [['coder', 'reviewer']];
|
|
7
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>`.";
|
|
8
9
|
const INDEPENDENT_PROPOSAL_PROMPT = [
|
|
9
10
|
'> <caller-topic>',
|
|
@@ -22,7 +23,7 @@ const COMMIT_CODER_PROMPT = [
|
|
|
22
23
|
'',
|
|
23
24
|
'Commit the result as one new commit, following @specs/packages/git.md.',
|
|
24
25
|
'Make the commit message explain concisely what changed and why.',
|
|
25
|
-
'
|
|
26
|
+
'Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.',
|
|
26
27
|
'Coder is <coder-llm>; format the model token in conventional human form.',
|
|
27
28
|
].join('\n');
|
|
28
29
|
const REVIEW_INPUT_TEMPLATE = [
|
|
@@ -275,9 +276,18 @@ function resumableStates(ids) {
|
|
|
275
276
|
}));
|
|
276
277
|
}
|
|
277
278
|
const stateMetadata = {
|
|
278
|
-
askCoderProposal: {
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
askCoderProposal: {
|
|
280
|
+
sourceItem: 'DECIDE-1',
|
|
281
|
+
asker: { kind: 'role', roleId: 'coder' },
|
|
282
|
+
},
|
|
283
|
+
askReviewerProposal: {
|
|
284
|
+
sourceItem: 'DECIDE-2',
|
|
285
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
286
|
+
},
|
|
287
|
+
commitCoderProposal: {
|
|
288
|
+
sourceItem: 'DECIDE-3',
|
|
289
|
+
asker: { kind: 'role', roleId: 'coder' },
|
|
290
|
+
},
|
|
281
291
|
};
|
|
282
292
|
export const decideMachine = setup({
|
|
283
293
|
types: {
|
|
@@ -424,9 +434,7 @@ export const decideMachine = setup({
|
|
|
424
434
|
}).createMachine({
|
|
425
435
|
id: 'decide',
|
|
426
436
|
initial: 'ready',
|
|
427
|
-
context: (
|
|
428
|
-
coderLlm: input.coderLlm,
|
|
429
|
-
}),
|
|
437
|
+
context: () => ({}),
|
|
430
438
|
output: ({ context }) => {
|
|
431
439
|
if (context.reviewResult)
|
|
432
440
|
return context.reviewResult;
|
|
@@ -486,7 +494,7 @@ export const decideMachine = setup({
|
|
|
486
494
|
playbook: {
|
|
487
495
|
stateId: 'askCoderProposal',
|
|
488
496
|
description: 'Coder independently proposes a spec design.',
|
|
489
|
-
|
|
497
|
+
role: 'coder',
|
|
490
498
|
},
|
|
491
499
|
},
|
|
492
500
|
invoke: {
|
|
@@ -494,7 +502,7 @@ export const decideMachine = setup({
|
|
|
494
502
|
input: ({ context }) => ({
|
|
495
503
|
stateId: 'askCoderProposal',
|
|
496
504
|
sourceItem: 'DECIDE-1',
|
|
497
|
-
|
|
505
|
+
role: 'coder',
|
|
498
506
|
prompt: INDEPENDENT_PROPOSAL_PROMPT,
|
|
499
507
|
result: withNeedsBossReply({
|
|
500
508
|
proposed: 'Coder completed an independent proposal. Output shall include `coderProposal: <verbatim final text>`.',
|
|
@@ -602,7 +610,7 @@ export const decideMachine = setup({
|
|
|
602
610
|
playbook: {
|
|
603
611
|
stateId: 'askReviewerProposal',
|
|
604
612
|
description: 'Reviewer independently proposes a spec design.',
|
|
605
|
-
|
|
613
|
+
role: 'reviewer',
|
|
606
614
|
},
|
|
607
615
|
},
|
|
608
616
|
invoke: {
|
|
@@ -610,7 +618,7 @@ export const decideMachine = setup({
|
|
|
610
618
|
input: ({ context }) => ({
|
|
611
619
|
stateId: 'askReviewerProposal',
|
|
612
620
|
sourceItem: 'DECIDE-2',
|
|
613
|
-
|
|
621
|
+
role: 'reviewer',
|
|
614
622
|
prompt: INDEPENDENT_PROPOSAL_PROMPT,
|
|
615
623
|
result: withNeedsBossReply({
|
|
616
624
|
proposed: 'Reviewer completed an independent proposal. Output shall include `reviewerProposal: <verbatim final text>`.',
|
|
@@ -721,7 +729,7 @@ export const decideMachine = setup({
|
|
|
721
729
|
playbook: {
|
|
722
730
|
stateId: 'commitCoderProposal',
|
|
723
731
|
description: 'Coder writes and commits Coder’s independent proposal.',
|
|
724
|
-
|
|
732
|
+
role: 'coder',
|
|
725
733
|
},
|
|
726
734
|
},
|
|
727
735
|
invoke: {
|
|
@@ -729,12 +737,11 @@ export const decideMachine = setup({
|
|
|
729
737
|
input: ({ context }) => ({
|
|
730
738
|
stateId: 'commitCoderProposal',
|
|
731
739
|
sourceItem: 'DECIDE-3',
|
|
732
|
-
|
|
740
|
+
role: 'coder',
|
|
733
741
|
prompt: COMMIT_CODER_PROMPT,
|
|
734
742
|
result: withNeedsBossReply({
|
|
735
743
|
committed: "Coder committed Coder's proposal. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
|
|
736
744
|
}),
|
|
737
|
-
coderLlm: context.coderLlm,
|
|
738
745
|
...bossReplyFields(context, 'commitCoderProposal'),
|
|
739
746
|
}),
|
|
740
747
|
onDone: [
|
|
@@ -15,7 +15,9 @@ export type JsonValue =
|
|
|
15
15
|
| readonly JsonValue[]
|
|
16
16
|
| { readonly [key: string]: JsonValue };
|
|
17
17
|
|
|
18
|
-
type
|
|
18
|
+
type Role = 'coder' | 'reviewer';
|
|
19
|
+
|
|
20
|
+
export const concurrentRoleSets = [['coder', 'reviewer']] as const;
|
|
19
21
|
|
|
20
22
|
type JumpableStateId = 'independentProposals';
|
|
21
23
|
|
|
@@ -28,7 +30,7 @@ export interface PendingBossQuestion {
|
|
|
28
30
|
questionId: ResumableStateId;
|
|
29
31
|
resumeStateId: ResumableStateId;
|
|
30
32
|
sourceItem: string;
|
|
31
|
-
|
|
33
|
+
asker: { kind: 'role'; roleId: Role };
|
|
32
34
|
question: string;
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -67,7 +69,6 @@ interface ChildFailure {
|
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
export interface DecideContext {
|
|
70
|
-
coderLlm: string;
|
|
71
72
|
callerTopic?: string;
|
|
72
73
|
coderProposal?: string;
|
|
73
74
|
reviewerProposal?: string;
|
|
@@ -95,18 +96,15 @@ export type DecideEvent =
|
|
|
95
96
|
answer: string;
|
|
96
97
|
};
|
|
97
98
|
|
|
98
|
-
export
|
|
99
|
-
coderLlm: string;
|
|
100
|
-
}
|
|
99
|
+
export type DecideInput = Readonly<Record<string, never>>;
|
|
101
100
|
|
|
102
101
|
export interface PlayerInput {
|
|
103
102
|
stateId: ResumableStateId;
|
|
104
103
|
sourceItem: string;
|
|
105
104
|
prompt: string;
|
|
106
105
|
result: Record<string, string>;
|
|
107
|
-
|
|
106
|
+
role: Role;
|
|
108
107
|
callerTopic?: string;
|
|
109
|
-
coderLlm?: string;
|
|
110
108
|
pendingBossQuestion?: PendingBossQuestion;
|
|
111
109
|
bossReply?: string;
|
|
112
110
|
}
|
|
@@ -159,7 +157,7 @@ const COMMIT_CODER_PROMPT = [
|
|
|
159
157
|
'',
|
|
160
158
|
'Commit the result as one new commit, following @specs/packages/git.md.',
|
|
161
159
|
'Make the commit message explain concisely what changed and why.',
|
|
162
|
-
'
|
|
160
|
+
'Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.',
|
|
163
161
|
'Coder is <coder-llm>; format the model token in conventional human form.',
|
|
164
162
|
].join('\n');
|
|
165
163
|
|
|
@@ -503,11 +501,20 @@ function resumableStates(ids: readonly ResumableStateId[]): any[] {
|
|
|
503
501
|
|
|
504
502
|
const stateMetadata: Record<
|
|
505
503
|
ResumableStateId,
|
|
506
|
-
{ sourceItem: string;
|
|
504
|
+
{ sourceItem: string; asker: { kind: 'role'; roleId: Role } }
|
|
507
505
|
> = {
|
|
508
|
-
askCoderProposal: {
|
|
509
|
-
|
|
510
|
-
|
|
506
|
+
askCoderProposal: {
|
|
507
|
+
sourceItem: 'DECIDE-1',
|
|
508
|
+
asker: { kind: 'role', roleId: 'coder' },
|
|
509
|
+
},
|
|
510
|
+
askReviewerProposal: {
|
|
511
|
+
sourceItem: 'DECIDE-2',
|
|
512
|
+
asker: { kind: 'role', roleId: 'reviewer' },
|
|
513
|
+
},
|
|
514
|
+
commitCoderProposal: {
|
|
515
|
+
sourceItem: 'DECIDE-3',
|
|
516
|
+
asker: { kind: 'role', roleId: 'coder' },
|
|
517
|
+
},
|
|
511
518
|
};
|
|
512
519
|
|
|
513
520
|
export const decideMachine = setup({
|
|
@@ -671,9 +678,7 @@ export const decideMachine = setup({
|
|
|
671
678
|
}).createMachine({
|
|
672
679
|
id: 'decide',
|
|
673
680
|
initial: 'ready',
|
|
674
|
-
context: (
|
|
675
|
-
coderLlm: input.coderLlm,
|
|
676
|
-
}),
|
|
681
|
+
context: (): DecideContext => ({}),
|
|
677
682
|
output: ({ context }): DecideOutput => {
|
|
678
683
|
if (context.reviewResult) return context.reviewResult;
|
|
679
684
|
if (!context.latestCommit || !context.reviewFailure) {
|
|
@@ -732,7 +737,7 @@ export const decideMachine = setup({
|
|
|
732
737
|
playbook: {
|
|
733
738
|
stateId: 'askCoderProposal',
|
|
734
739
|
description: 'Coder independently proposes a spec design.',
|
|
735
|
-
|
|
740
|
+
role: 'coder',
|
|
736
741
|
},
|
|
737
742
|
},
|
|
738
743
|
invoke: {
|
|
@@ -740,7 +745,7 @@ export const decideMachine = setup({
|
|
|
740
745
|
input: ({ context }): PlayerInput => ({
|
|
741
746
|
stateId: 'askCoderProposal',
|
|
742
747
|
sourceItem: 'DECIDE-1',
|
|
743
|
-
|
|
748
|
+
role: 'coder',
|
|
744
749
|
prompt: INDEPENDENT_PROPOSAL_PROMPT,
|
|
745
750
|
result: withNeedsBossReply({
|
|
746
751
|
proposed:
|
|
@@ -851,7 +856,7 @@ export const decideMachine = setup({
|
|
|
851
856
|
stateId: 'askReviewerProposal',
|
|
852
857
|
description:
|
|
853
858
|
'Reviewer independently proposes a spec design.',
|
|
854
|
-
|
|
859
|
+
role: 'reviewer',
|
|
855
860
|
},
|
|
856
861
|
},
|
|
857
862
|
invoke: {
|
|
@@ -859,7 +864,7 @@ export const decideMachine = setup({
|
|
|
859
864
|
input: ({ context }): PlayerInput => ({
|
|
860
865
|
stateId: 'askReviewerProposal',
|
|
861
866
|
sourceItem: 'DECIDE-2',
|
|
862
|
-
|
|
867
|
+
role: 'reviewer',
|
|
863
868
|
prompt: INDEPENDENT_PROPOSAL_PROMPT,
|
|
864
869
|
result: withNeedsBossReply({
|
|
865
870
|
proposed:
|
|
@@ -974,7 +979,7 @@ export const decideMachine = setup({
|
|
|
974
979
|
playbook: {
|
|
975
980
|
stateId: 'commitCoderProposal',
|
|
976
981
|
description: 'Coder writes and commits Coder’s independent proposal.',
|
|
977
|
-
|
|
982
|
+
role: 'coder',
|
|
978
983
|
},
|
|
979
984
|
},
|
|
980
985
|
invoke: {
|
|
@@ -982,13 +987,12 @@ export const decideMachine = setup({
|
|
|
982
987
|
input: ({ context }): PlayerInput => ({
|
|
983
988
|
stateId: 'commitCoderProposal',
|
|
984
989
|
sourceItem: 'DECIDE-3',
|
|
985
|
-
|
|
990
|
+
role: 'coder',
|
|
986
991
|
prompt: COMMIT_CODER_PROMPT,
|
|
987
992
|
result: withNeedsBossReply({
|
|
988
993
|
committed:
|
|
989
994
|
"Coder committed Coder's proposal. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
|
|
990
995
|
}),
|
|
991
|
-
coderLlm: context.coderLlm,
|
|
992
996
|
...bossReplyFields(context, 'commitCoderProposal'),
|
|
993
997
|
}),
|
|
994
998
|
onDone: [
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
# Decide
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Roles
|
|
7
7
|
|
|
8
|
-
- Boss: the human user (default)
|
|
9
|
-
- Captain: the coordinating agent (default)
|
|
10
8
|
- Coder
|
|
11
9
|
- Reviewer
|
|
12
10
|
|
|
@@ -14,7 +12,7 @@
|
|
|
14
12
|
|
|
15
13
|
The caller supplies a topic as `callerTopic`.
|
|
16
14
|
Coder and Reviewer receive the complete topic concurrently and independently.
|
|
17
|
-
Neither player receives the other
|
|
15
|
+
Neither role's player receives the other role's proposal before both proposals finish and Coder commits Coder's own proposal.
|
|
18
16
|
A Boss interrupt during the parallel proposal pair restarts the complete pair with the new `callerTopic`.
|
|
19
17
|
|
|
20
18
|
### DECIDE-1
|
|
@@ -64,7 +62,7 @@ When both independent proposals are complete, Captain shall prompt Coder:
|
|
|
64
62
|
>
|
|
65
63
|
> Commit the result as one new commit, following @specs/packages/git.md.
|
|
66
64
|
> Make the commit message explain concisely what changed and why.
|
|
67
|
-
>
|
|
65
|
+
> Include exactly one final-response line beginning `Commit: `, followed only by the exact commit identity; other final-response content may appear on other lines.
|
|
68
66
|
> Coder is <coder-llm>; format the model token in conventional human form.
|
|
69
67
|
|
|
70
68
|
Results:
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { type PlayerInput, type PlayerOutput, type DecideEvent, type DecideInput, type PendingBossQuestion } from './decide.fsm.js';
|
|
2
2
|
import type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlayerResult, PlayerSessionStore, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlAction, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType } from '@sublang/playbook/runtime';
|
|
3
3
|
export type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlayerResult, PlayerSessionStore, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlAction, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, };
|
|
4
|
-
type
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
declare function composePlayerPrompt(input: PlayerInput): string;
|
|
9
|
-
declare function resolvePlayerId(input: PlayerInput, binding: Record<PlayerName, string>): string;
|
|
4
|
+
type RoleId = 'coder' | 'reviewer';
|
|
5
|
+
export type PlaybookRuntimeOptions = DecideInput;
|
|
6
|
+
type PromptIdentity = (roleId: RoleId) => string;
|
|
7
|
+
declare function composePlayerPrompt(input: PlayerInput, promptIdentity: PromptIdentity): string;
|
|
10
8
|
declare function requiredFieldsFor(description: string): string[];
|
|
11
9
|
declare function extractJson(raw: string): Record<string, unknown> | null;
|
|
12
10
|
declare function buildClassifierPrompt(text: string, ctx: {
|
|
@@ -32,7 +30,6 @@ declare function pendingQuestionsFromContext(context: Record<string, unknown>):
|
|
|
32
30
|
export declare const createPlaybookRuntime: PlaybookRuntimeFactory<PlaybookRuntimeOptions>;
|
|
33
31
|
export declare const _internal: {
|
|
34
32
|
composePlayerPrompt: typeof composePlayerPrompt;
|
|
35
|
-
resolvePlayerId: typeof resolvePlayerId;
|
|
36
33
|
requiredFieldsFor: typeof requiredFieldsFor;
|
|
37
34
|
extractJson: typeof extractJson;
|
|
38
35
|
buildClassifierPrompt: typeof buildClassifierPrompt;
|
|
@@ -43,22 +40,21 @@ export declare const _internal: {
|
|
|
43
40
|
pendingQuestionsFromContext: typeof pendingQuestionsFromContext;
|
|
44
41
|
normalizeErrorCompact: typeof normalizeErrorCompact;
|
|
45
42
|
normalizeErrorFull: typeof normalizeErrorFull;
|
|
46
|
-
DEFAULT_PLAYER_BINDING: Readonly<Record<PlayerName, string>>;
|
|
47
43
|
STATE_DESCRIPTIONS: Readonly<Record<string, string>>;
|
|
48
|
-
|
|
44
|
+
ROLE_STATES: readonly [{
|
|
49
45
|
readonly stateId: "askCoderProposal";
|
|
50
|
-
readonly
|
|
46
|
+
readonly role: "coder";
|
|
51
47
|
readonly sourceItem: "DECIDE-1";
|
|
52
48
|
}, {
|
|
53
49
|
readonly stateId: "askReviewerProposal";
|
|
54
|
-
readonly
|
|
50
|
+
readonly role: "reviewer";
|
|
55
51
|
readonly sourceItem: "DECIDE-2";
|
|
56
52
|
}, {
|
|
57
53
|
readonly stateId: "commitCoderProposal";
|
|
58
|
-
readonly
|
|
54
|
+
readonly role: "coder";
|
|
59
55
|
readonly sourceItem: "DECIDE-3";
|
|
60
56
|
}];
|
|
61
|
-
|
|
57
|
+
ROLE_STATE_IDS: ReadonlySet<string>;
|
|
62
58
|
VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
|
|
63
59
|
BOSS_INTERRUPT_TARGETS: readonly ["independentProposals"];
|
|
64
60
|
CONTINUATION_PREAMBLE: string;
|