@sublang/playbook 4.0.0 → 6.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 +69 -122
- package/docs/assets/playbook-venn.svg +13 -0
- package/docs/cli.md +43 -26
- package/docs/configuration.md +63 -18
- package/docs/embedding.md +24 -16
- package/package.json +43 -22
- package/reference/sdlc/captain.md +70 -83
- package/reference/sdlc/captain.playbook/captain.fsm.d.ts +127 -142
- package/reference/sdlc/captain.playbook/captain.fsm.js +349 -470
- package/reference/sdlc/captain.playbook/captain.fsm.ts +535 -598
- package/reference/sdlc/captain.playbook/captain.gears.md +37 -41
- package/reference/sdlc/captain.playbook/captain.playbook.d.ts +90 -15
- package/reference/sdlc/captain.playbook/captain.playbook.js +466 -976
- package/reference/sdlc/captain.playbook/captain.playbook.ts +698 -1001
- package/reference/sdlc/code.md +55 -97
- package/reference/sdlc/code.playbook/code.fsm.d.ts +229 -94
- package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +26 -44
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +61 -66
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +100 -149
- package/reference/sdlc/code.playbook/code.fsm.js +587 -1347
- package/reference/sdlc/code.playbook/code.fsm.ts +809 -1650
- package/reference/sdlc/code.playbook/code.gears.md +51 -263
- package/reference/sdlc/code.playbook/code.playbook.d.ts +8 -47
- package/reference/sdlc/code.playbook/code.playbook.js +69 -639
- package/reference/sdlc/code.playbook/code.playbook.ts +90 -850
- package/reference/sdlc/code.playbook/code.registry.d.ts +9 -25
- package/reference/sdlc/code.playbook/code.registry.js +20 -78
- package/reference/sdlc/code.playbook/code.registry.ts +58 -122
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +2 -0
- package/reference/sdlc/code.playbook/playbook-captain.js +1877 -251
- package/reference/sdlc/code.playbook/playbook-captain.ts +2385 -352
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +33 -22
- package/reference/sdlc/decide.md +54 -0
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +261 -0
- package/reference/sdlc/decide.playbook/decide.fsm.js +894 -0
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1152 -0
- package/reference/sdlc/decide.playbook/decide.gears.md +88 -0
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +67 -0
- package/reference/sdlc/{discuss.playbook/discuss.playbook.js → decide.playbook/decide.playbook.js} +511 -370
- package/reference/sdlc/{discuss.playbook/discuss.playbook.ts → decide.playbook/decide.playbook.ts} +616 -451
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +41 -0
- package/reference/sdlc/decide.playbook/decide.registry.js +60 -0
- package/reference/sdlc/decide.playbook/decide.registry.ts +125 -0
- package/reference/sdlc/review.md +81 -0
- package/reference/sdlc/review.playbook/review.fsm.d.ts +183 -0
- package/reference/sdlc/review.playbook/review.fsm.js +524 -0
- package/reference/sdlc/review.playbook/review.fsm.ts +652 -0
- package/reference/sdlc/review.playbook/review.gears.md +112 -0
- package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -0
- package/reference/sdlc/review.playbook/review.playbook.js +112 -0
- package/reference/sdlc/review.playbook/review.playbook.ts +201 -0
- package/reference/sdlc/review.playbook/review.registry.d.ts +43 -0
- package/reference/sdlc/review.playbook/review.registry.js +73 -0
- package/reference/sdlc/review.playbook/review.registry.ts +138 -0
- package/slc/gears2fsm.md +67 -6
- package/slc/link.md +339 -25
- package/slc/text2gears.md +22 -2
- package/src/runtime.d.ts +36 -1
- package/src/runtime.ts +59 -0
- package/src/xstate-playbook-runtime.d.ts +96 -7
- package/src/xstate-playbook-runtime.js +1018 -49
- package/src/xstate-playbook-runtime.ts +1283 -59
- package/src/xstate-runtime.js +25 -0
- package/src/xstate-runtime.ts +51 -0
- package/reference/sdlc/discuss.md +0 -93
- package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +0 -396
- package/reference/sdlc/discuss.playbook/discuss.fsm.js +0 -2067
- package/reference/sdlc/discuss.playbook/discuss.fsm.ts +0 -2465
- package/reference/sdlc/discuss.playbook/discuss.gears.md +0 -258
- package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +0 -113
- package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +0 -58
- package/reference/sdlc/discuss.playbook/discuss.registry.js +0 -97
- package/reference/sdlc/discuss.playbook/discuss.registry.ts +0 -153
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
import { type CodePlaybookOptions, type PlaybookRuntime } from './code.playbook.js';
|
|
2
|
-
export declare const codeCopyPasteGuardNames: readonly ["accepted", "approved", "challengeAccepted", "challengeRejected", "challengesRaised", "changesMadeCode", "changesMadeCodeAndChallenged", "changesMadeMixed", "changesMadeMixedAndChallenged", "changesMadeSpecs", "changesMadeSpecsAndChallenged", "hasFindings", "needsRevision", "noFindings", "noOpenItems"];
|
|
3
|
-
export declare const codeStateCountLabels: {
|
|
4
|
-
readonly adjudicateChallenges: "rebuttal";
|
|
5
|
-
readonly reviewBossCommitSpecs: "review round";
|
|
6
|
-
readonly reviewBossCommitCode: "review round";
|
|
7
|
-
readonly reviewBossCommitMixed: "review round";
|
|
8
|
-
readonly reviewIrTaskCommitSpecs: "review round";
|
|
9
|
-
readonly reviewIrTaskCommitCode: "review round";
|
|
10
|
-
readonly reviewIrTaskCommitMixed: "review round";
|
|
11
|
-
readonly reviewChangesSpecs: "review round";
|
|
12
|
-
readonly reviewChangesCode: "review round";
|
|
13
|
-
readonly reviewChangesMixed: "review round";
|
|
14
|
-
readonly reviewChangesAndChallengesSpecs: "review round";
|
|
15
|
-
readonly reviewChangesAndChallengesCode: "review round";
|
|
16
|
-
readonly reviewChangesAndChallengesMixed: "review round";
|
|
17
|
-
};
|
|
18
|
-
export declare function codeSavedCountsLine(counts: {
|
|
19
|
-
interruptions: number;
|
|
20
|
-
copyPastes: number;
|
|
21
|
-
}, rounds: number): string;
|
|
22
2
|
export interface PlaybookSummaryPolicy {
|
|
23
3
|
stateCountLabels: Readonly<Record<string, string>>;
|
|
24
4
|
copyPasteGuardNames: readonly string[];
|
|
@@ -27,10 +7,6 @@ export interface PlaybookSummaryPolicy {
|
|
|
27
7
|
copyPastes: number;
|
|
28
8
|
}, rounds: number): string;
|
|
29
9
|
}
|
|
30
|
-
export declare const codeSummaryPolicy: PlaybookSummaryPolicy;
|
|
31
|
-
export interface CodeOptions {
|
|
32
|
-
committer?: 'coder' | 'reviewer';
|
|
33
|
-
}
|
|
34
10
|
export interface RegistryPlayer {
|
|
35
11
|
id: string;
|
|
36
12
|
adapter?: string;
|
|
@@ -40,15 +16,23 @@ export interface CreateCodeRuntimeOptions {
|
|
|
40
16
|
captainOptions: unknown;
|
|
41
17
|
players: readonly RegistryPlayer[];
|
|
42
18
|
}
|
|
19
|
+
export type CodeOptions = Readonly<Record<string, never>>;
|
|
43
20
|
export interface CodePlaybookRegistryEntry {
|
|
44
21
|
id: 'code';
|
|
45
22
|
command: 'code';
|
|
46
23
|
intent: string;
|
|
47
|
-
requiredRoleIds: readonly
|
|
24
|
+
requiredRoleIds: readonly ['coder'];
|
|
48
25
|
summaryPolicy: PlaybookSummaryPolicy;
|
|
49
26
|
validateOptions(captainOptions: unknown): CodeOptions;
|
|
50
27
|
createRuntime(options: CreateCodeRuntimeOptions): PlaybookRuntime;
|
|
51
28
|
}
|
|
29
|
+
export declare const codeStateCountLabels: {};
|
|
30
|
+
export declare const codeCopyPasteGuardNames: readonly ["directCommit", "irCommit", "moreTasks", "finalTask"];
|
|
31
|
+
export declare function codeSavedCountsLine(counts: {
|
|
32
|
+
interruptions: number;
|
|
33
|
+
copyPastes: number;
|
|
34
|
+
}, rounds: number): string;
|
|
35
|
+
export declare const codeSummaryPolicy: PlaybookSummaryPolicy;
|
|
52
36
|
export declare function validateCodeOptions(optionSlice: unknown): CodeOptions;
|
|
53
37
|
export declare function createCodeRuntimeOptions({ captainOptions, players, }: CreateCodeRuntimeOptions): CodePlaybookOptions;
|
|
54
38
|
export declare const codePlaybookRegistryEntry: CodePlaybookRegistryEntry;
|
|
@@ -1,55 +1,18 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
import createPlaybookRuntime from './code.playbook.js';
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
// key, `committer`: an optional Committer-alias player id, one of the
|
|
8
|
-
// baked role ids `coder` / `reviewer`. A valid slice is absent, `{}`, or
|
|
9
|
-
// `{ committer: 'coder' | 'reviewer' }`; every other key is unknown and
|
|
10
|
-
// rejected with a path-named error. A further CODE option shall be
|
|
11
|
-
// introduced as its own higher-numbered item that widens
|
|
12
|
-
// `CODE_OPTION_KEYS`; the validator still fails closed on stray keys.
|
|
13
|
-
const CODE_OPTION_KEYS = new Set(['committer']);
|
|
14
|
-
const COMMITTER_PLAYER_IDS = new Set(['coder', 'reviewer']);
|
|
4
|
+
// REVIEW owns and labels its real review rounds. CODE's two suspended wrapper
|
|
5
|
+
// states only delegate to that child and must not double-count those rounds.
|
|
6
|
+
export const codeStateCountLabels = {};
|
|
15
7
|
export const codeCopyPasteGuardNames = [
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'challengesRaised',
|
|
21
|
-
'changesMadeCode',
|
|
22
|
-
'changesMadeCodeAndChallenged',
|
|
23
|
-
'changesMadeMixed',
|
|
24
|
-
'changesMadeMixedAndChallenged',
|
|
25
|
-
'changesMadeSpecs',
|
|
26
|
-
'changesMadeSpecsAndChallenged',
|
|
27
|
-
'hasFindings',
|
|
28
|
-
'needsRevision',
|
|
29
|
-
'noFindings',
|
|
30
|
-
'noOpenItems',
|
|
8
|
+
'directCommit',
|
|
9
|
+
'irCommit',
|
|
10
|
+
'moreTasks',
|
|
11
|
+
'finalTask',
|
|
31
12
|
];
|
|
32
|
-
export const codeStateCountLabels = {
|
|
33
|
-
adjudicateChallenges: 'rebuttal',
|
|
34
|
-
reviewBossCommitSpecs: 'review round',
|
|
35
|
-
reviewBossCommitCode: 'review round',
|
|
36
|
-
reviewBossCommitMixed: 'review round',
|
|
37
|
-
reviewIrTaskCommitSpecs: 'review round',
|
|
38
|
-
reviewIrTaskCommitCode: 'review round',
|
|
39
|
-
reviewIrTaskCommitMixed: 'review round',
|
|
40
|
-
reviewChangesSpecs: 'review round',
|
|
41
|
-
reviewChangesCode: 'review round',
|
|
42
|
-
reviewChangesMixed: 'review round',
|
|
43
|
-
reviewChangesAndChallengesSpecs: 'review round',
|
|
44
|
-
reviewChangesAndChallengesCode: 'review round',
|
|
45
|
-
reviewChangesAndChallengesMixed: 'review round',
|
|
46
|
-
};
|
|
47
13
|
function countNoun(count, singular, plural = `${singular}s`) {
|
|
48
14
|
return `${count} ${count === 1 ? singular : plural}`;
|
|
49
15
|
}
|
|
50
|
-
// PBRT-15 / CAPTAIN-19: the CODE saved-counts line wording is
|
|
51
|
-
// registry-owned. The shell renders this exact line through the active
|
|
52
|
-
// entry's summary policy rather than hardcoding CODE phrasing.
|
|
53
16
|
export function codeSavedCountsLine(counts, rounds) {
|
|
54
17
|
return [
|
|
55
18
|
'Saved you',
|
|
@@ -68,57 +31,36 @@ export const codeSummaryPolicy = {
|
|
|
68
31
|
};
|
|
69
32
|
export function validateCodeOptions(optionSlice) {
|
|
70
33
|
if (optionSlice === undefined)
|
|
71
|
-
return {};
|
|
72
|
-
if (
|
|
73
|
-
optionSlice
|
|
34
|
+
return Object.freeze({});
|
|
35
|
+
if (optionSlice === null ||
|
|
36
|
+
typeof optionSlice !== 'object' ||
|
|
74
37
|
Array.isArray(optionSlice)) {
|
|
75
38
|
throw new Error('captain.options.playbooks.code.options must be an object');
|
|
76
39
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
throw new Error(`Unknown config field captain.options.playbooks.code.options.${key}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const options = {};
|
|
84
|
-
const committer = slice.committer;
|
|
85
|
-
if (committer !== undefined) {
|
|
86
|
-
if (typeof committer !== 'string' || !COMMITTER_PLAYER_IDS.has(committer)) {
|
|
87
|
-
throw new Error("captain.options.playbooks.code.options.committer must be " +
|
|
88
|
-
"'coder' or 'reviewer'");
|
|
89
|
-
}
|
|
90
|
-
options.committer = committer;
|
|
40
|
+
const keys = Object.keys(optionSlice);
|
|
41
|
+
if (keys.length > 0) {
|
|
42
|
+
throw new Error(`Unknown config field captain.options.playbooks.code.options.${keys[0]}`);
|
|
91
43
|
}
|
|
92
|
-
return
|
|
44
|
+
return Object.freeze({});
|
|
93
45
|
}
|
|
94
46
|
function playerIdentity(players, id) {
|
|
95
|
-
const
|
|
96
|
-
return
|
|
47
|
+
const player = players.find((entry) => entry.id === id);
|
|
48
|
+
return player?.model ?? player?.adapter;
|
|
97
49
|
}
|
|
98
50
|
export function createCodeRuntimeOptions({ captainOptions, players, }) {
|
|
99
|
-
|
|
51
|
+
validateCodeOptions(captainOptions);
|
|
100
52
|
const coderPlayer = playerIdentity(players, 'coder');
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
coderPlayer,
|
|
104
|
-
reviewerPlayer,
|
|
105
|
-
...(codeOptions.committer !== undefined
|
|
106
|
-
? { committerPlayer: codeOptions.committer }
|
|
107
|
-
: {}),
|
|
108
|
-
};
|
|
53
|
+
return coderPlayer === undefined ? {} : { coderPlayer };
|
|
109
54
|
}
|
|
110
55
|
export const codePlaybookRegistryEntry = {
|
|
111
56
|
id: 'code',
|
|
112
57
|
command: 'code',
|
|
113
|
-
intent: '
|
|
114
|
-
requiredRoleIds: ['coder'
|
|
58
|
+
intent: 'implement a coding intent in reviewed, one-commit phases, using an intent record when needed',
|
|
59
|
+
requiredRoleIds: ['coder'],
|
|
115
60
|
summaryPolicy: codeSummaryPolicy,
|
|
116
61
|
validateOptions: validateCodeOptions,
|
|
117
62
|
createRuntime(options) {
|
|
118
63
|
return createPlaybookRuntime(createCodeRuntimeOptions(options));
|
|
119
64
|
},
|
|
120
65
|
};
|
|
121
|
-
// CAPTAIN-16 / PBRT-16: the published `@sublang/playbook/code/registry`
|
|
122
|
-
// module's default export is the CODE registry entry the Playbook Captain
|
|
123
|
-
// shell loads when a playbook block's `from` names this module.
|
|
124
66
|
export default codePlaybookRegistryEntry;
|
|
@@ -6,77 +6,6 @@ import createPlaybookRuntime, {
|
|
|
6
6
|
type PlaybookRuntime,
|
|
7
7
|
} from './code.playbook.js';
|
|
8
8
|
|
|
9
|
-
// PBRT-30: the CODE registry entry validates the option slice the shell
|
|
10
|
-
// passes it (`captain.options.playbooks.code.options`), not a namespace
|
|
11
|
-
// it extracts from the full Captain options bag. The schema defines one
|
|
12
|
-
// key, `committer`: an optional Committer-alias player id, one of the
|
|
13
|
-
// baked role ids `coder` / `reviewer`. A valid slice is absent, `{}`, or
|
|
14
|
-
// `{ committer: 'coder' | 'reviewer' }`; every other key is unknown and
|
|
15
|
-
// rejected with a path-named error. A further CODE option shall be
|
|
16
|
-
// introduced as its own higher-numbered item that widens
|
|
17
|
-
// `CODE_OPTION_KEYS`; the validator still fails closed on stray keys.
|
|
18
|
-
const CODE_OPTION_KEYS = new Set<string>(['committer']);
|
|
19
|
-
const COMMITTER_PLAYER_IDS = new Set<string>(['coder', 'reviewer']);
|
|
20
|
-
export const codeCopyPasteGuardNames = [
|
|
21
|
-
'accepted',
|
|
22
|
-
'approved',
|
|
23
|
-
'challengeAccepted',
|
|
24
|
-
'challengeRejected',
|
|
25
|
-
'challengesRaised',
|
|
26
|
-
'changesMadeCode',
|
|
27
|
-
'changesMadeCodeAndChallenged',
|
|
28
|
-
'changesMadeMixed',
|
|
29
|
-
'changesMadeMixedAndChallenged',
|
|
30
|
-
'changesMadeSpecs',
|
|
31
|
-
'changesMadeSpecsAndChallenged',
|
|
32
|
-
'hasFindings',
|
|
33
|
-
'needsRevision',
|
|
34
|
-
'noFindings',
|
|
35
|
-
'noOpenItems',
|
|
36
|
-
] as const;
|
|
37
|
-
|
|
38
|
-
export const codeStateCountLabels = {
|
|
39
|
-
adjudicateChallenges: 'rebuttal',
|
|
40
|
-
reviewBossCommitSpecs: 'review round',
|
|
41
|
-
reviewBossCommitCode: 'review round',
|
|
42
|
-
reviewBossCommitMixed: 'review round',
|
|
43
|
-
reviewIrTaskCommitSpecs: 'review round',
|
|
44
|
-
reviewIrTaskCommitCode: 'review round',
|
|
45
|
-
reviewIrTaskCommitMixed: 'review round',
|
|
46
|
-
reviewChangesSpecs: 'review round',
|
|
47
|
-
reviewChangesCode: 'review round',
|
|
48
|
-
reviewChangesMixed: 'review round',
|
|
49
|
-
reviewChangesAndChallengesSpecs: 'review round',
|
|
50
|
-
reviewChangesAndChallengesCode: 'review round',
|
|
51
|
-
reviewChangesAndChallengesMixed: 'review round',
|
|
52
|
-
} as const;
|
|
53
|
-
|
|
54
|
-
function countNoun(count: number, singular: string, plural = `${singular}s`): string {
|
|
55
|
-
return `${count} ${count === 1 ? singular : plural}`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// PBRT-15 / CAPTAIN-19: the CODE saved-counts line wording is
|
|
59
|
-
// registry-owned. The shell renders this exact line through the active
|
|
60
|
-
// entry's summary policy rather than hardcoding CODE phrasing.
|
|
61
|
-
export function codeSavedCountsLine(
|
|
62
|
-
counts: { interruptions: number; copyPastes: number },
|
|
63
|
-
rounds: number,
|
|
64
|
-
): string {
|
|
65
|
-
return [
|
|
66
|
-
'Saved you',
|
|
67
|
-
countNoun(counts.interruptions, 'interruption'),
|
|
68
|
-
'and',
|
|
69
|
-
countNoun(counts.copyPastes, 'copy-paste'),
|
|
70
|
-
'across',
|
|
71
|
-
countNoun(rounds, 'round'),
|
|
72
|
-
'of reviews/rebuttals.',
|
|
73
|
-
].join(' ');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// CAPTAIN-20 summary policy: the counted state-id labels, the
|
|
77
|
-
// copy-paste guard names, and the saved-counts line wording an entry
|
|
78
|
-
// owns for the shell's turn-summary aggregation. An entry without a
|
|
79
|
-
// summary policy opts out of visible turn summaries.
|
|
80
9
|
export interface PlaybookSummaryPolicy {
|
|
81
10
|
stateCountLabels: Readonly<Record<string, string>>;
|
|
82
11
|
copyPasteGuardNames: readonly string[];
|
|
@@ -86,19 +15,6 @@ export interface PlaybookSummaryPolicy {
|
|
|
86
15
|
): string;
|
|
87
16
|
}
|
|
88
17
|
|
|
89
|
-
export const codeSummaryPolicy: PlaybookSummaryPolicy = {
|
|
90
|
-
stateCountLabels: codeStateCountLabels,
|
|
91
|
-
copyPasteGuardNames: codeCopyPasteGuardNames,
|
|
92
|
-
savedCountsLine: codeSavedCountsLine,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// The validated CODE options set. `committer`, when present, is the
|
|
96
|
-
// resolved Committer-alias player id (PBRT-8 / PBRT-30); a future CODE
|
|
97
|
-
// option widens both this type and `CODE_OPTION_KEYS`.
|
|
98
|
-
export interface CodeOptions {
|
|
99
|
-
committer?: 'coder' | 'reviewer';
|
|
100
|
-
}
|
|
101
|
-
|
|
102
18
|
export interface RegistryPlayer {
|
|
103
19
|
id: string;
|
|
104
20
|
adapter?: string;
|
|
@@ -110,76 +26,99 @@ export interface CreateCodeRuntimeOptions {
|
|
|
110
26
|
players: readonly RegistryPlayer[];
|
|
111
27
|
}
|
|
112
28
|
|
|
29
|
+
export type CodeOptions = Readonly<Record<string, never>>;
|
|
30
|
+
|
|
113
31
|
export interface CodePlaybookRegistryEntry {
|
|
114
32
|
id: 'code';
|
|
115
33
|
command: 'code';
|
|
116
34
|
intent: string;
|
|
117
|
-
requiredRoleIds: readonly
|
|
35
|
+
requiredRoleIds: readonly ['coder'];
|
|
118
36
|
summaryPolicy: PlaybookSummaryPolicy;
|
|
119
37
|
validateOptions(captainOptions: unknown): CodeOptions;
|
|
120
38
|
createRuntime(options: CreateCodeRuntimeOptions): PlaybookRuntime;
|
|
121
39
|
}
|
|
122
40
|
|
|
41
|
+
// REVIEW owns and labels its real review rounds. CODE's two suspended wrapper
|
|
42
|
+
// states only delegate to that child and must not double-count those rounds.
|
|
43
|
+
export const codeStateCountLabels = {} as const;
|
|
44
|
+
|
|
45
|
+
export const codeCopyPasteGuardNames = [
|
|
46
|
+
'directCommit',
|
|
47
|
+
'irCommit',
|
|
48
|
+
'moreTasks',
|
|
49
|
+
'finalTask',
|
|
50
|
+
] as const;
|
|
51
|
+
|
|
52
|
+
function countNoun(
|
|
53
|
+
count: number,
|
|
54
|
+
singular: string,
|
|
55
|
+
plural = `${singular}s`,
|
|
56
|
+
): string {
|
|
57
|
+
return `${count} ${count === 1 ? singular : plural}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function codeSavedCountsLine(
|
|
61
|
+
counts: { interruptions: number; copyPastes: number },
|
|
62
|
+
rounds: number,
|
|
63
|
+
): string {
|
|
64
|
+
return [
|
|
65
|
+
'Saved you',
|
|
66
|
+
countNoun(counts.interruptions, 'interruption'),
|
|
67
|
+
'and',
|
|
68
|
+
countNoun(counts.copyPastes, 'copy-paste'),
|
|
69
|
+
'across',
|
|
70
|
+
countNoun(rounds, 'round'),
|
|
71
|
+
'of reviews/rebuttals.',
|
|
72
|
+
].join(' ');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const codeSummaryPolicy: PlaybookSummaryPolicy = {
|
|
76
|
+
stateCountLabels: codeStateCountLabels,
|
|
77
|
+
copyPasteGuardNames: codeCopyPasteGuardNames,
|
|
78
|
+
savedCountsLine: codeSavedCountsLine,
|
|
79
|
+
};
|
|
80
|
+
|
|
123
81
|
export function validateCodeOptions(optionSlice: unknown): CodeOptions {
|
|
124
|
-
if (optionSlice === undefined) return {};
|
|
82
|
+
if (optionSlice === undefined) return Object.freeze({});
|
|
125
83
|
if (
|
|
126
|
-
typeof optionSlice !== 'object' ||
|
|
127
84
|
optionSlice === null ||
|
|
85
|
+
typeof optionSlice !== 'object' ||
|
|
128
86
|
Array.isArray(optionSlice)
|
|
129
87
|
) {
|
|
130
88
|
throw new Error('captain.options.playbooks.code.options must be an object');
|
|
131
89
|
}
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const options: CodeOptions = {};
|
|
141
|
-
const committer = slice.committer;
|
|
142
|
-
if (committer !== undefined) {
|
|
143
|
-
if (typeof committer !== 'string' || !COMMITTER_PLAYER_IDS.has(committer)) {
|
|
144
|
-
throw new Error(
|
|
145
|
-
"captain.options.playbooks.code.options.committer must be " +
|
|
146
|
-
"'coder' or 'reviewer'",
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
options.committer = committer as 'coder' | 'reviewer';
|
|
90
|
+
const keys = Object.keys(optionSlice);
|
|
91
|
+
if (keys.length > 0) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Unknown config field captain.options.playbooks.code.options.${keys[0]}`,
|
|
94
|
+
);
|
|
150
95
|
}
|
|
151
|
-
return
|
|
96
|
+
return Object.freeze({});
|
|
152
97
|
}
|
|
153
98
|
|
|
154
99
|
function playerIdentity(
|
|
155
100
|
players: readonly RegistryPlayer[],
|
|
156
101
|
id: string,
|
|
157
102
|
): string | undefined {
|
|
158
|
-
const
|
|
159
|
-
return
|
|
103
|
+
const player = players.find((entry) => entry.id === id);
|
|
104
|
+
return player?.model ?? player?.adapter;
|
|
160
105
|
}
|
|
161
106
|
|
|
162
107
|
export function createCodeRuntimeOptions({
|
|
163
108
|
captainOptions,
|
|
164
109
|
players,
|
|
165
110
|
}: CreateCodeRuntimeOptions): CodePlaybookOptions {
|
|
166
|
-
|
|
111
|
+
validateCodeOptions(captainOptions);
|
|
167
112
|
const coderPlayer = playerIdentity(players, 'coder');
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
coderPlayer,
|
|
171
|
-
reviewerPlayer,
|
|
172
|
-
...(codeOptions.committer !== undefined
|
|
173
|
-
? { committerPlayer: codeOptions.committer }
|
|
174
|
-
: {}),
|
|
175
|
-
};
|
|
113
|
+
return coderPlayer === undefined ? {} : { coderPlayer };
|
|
176
114
|
}
|
|
177
115
|
|
|
178
116
|
export const codePlaybookRegistryEntry: CodePlaybookRegistryEntry = {
|
|
179
117
|
id: 'code',
|
|
180
118
|
command: 'code',
|
|
181
|
-
intent:
|
|
182
|
-
|
|
119
|
+
intent:
|
|
120
|
+
'implement a coding intent in reviewed, one-commit phases, using an intent record when needed',
|
|
121
|
+
requiredRoleIds: ['coder'],
|
|
183
122
|
summaryPolicy: codeSummaryPolicy,
|
|
184
123
|
validateOptions: validateCodeOptions,
|
|
185
124
|
createRuntime(options) {
|
|
@@ -187,7 +126,4 @@ export const codePlaybookRegistryEntry: CodePlaybookRegistryEntry = {
|
|
|
187
126
|
},
|
|
188
127
|
};
|
|
189
128
|
|
|
190
|
-
// CAPTAIN-16 / PBRT-16: the published `@sublang/playbook/code/registry`
|
|
191
|
-
// module's default export is the CODE registry entry the Playbook Captain
|
|
192
|
-
// shell loads when a playbook block's `from` names this module.
|
|
193
129
|
export default codePlaybookRegistryEntry;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Captain } from '@sublang/cligent/tmux-play';
|
|
2
2
|
import type { PlaybookRuntime } from '@sublang/playbook/runtime';
|
|
3
|
+
import { type CaptainControllerPort } from '../captain.playbook/captain.playbook.js';
|
|
3
4
|
import type { PlaybookSummaryPolicy, RegistryPlayer } from './code.registry.js';
|
|
4
5
|
export interface CreatePlaybookRuntimeOptions {
|
|
5
6
|
captainOptions: unknown;
|
|
@@ -14,6 +15,7 @@ export interface PlaybookCaptainDeps {
|
|
|
14
15
|
readonly command: string;
|
|
15
16
|
readonly intent: string;
|
|
16
17
|
}[];
|
|
18
|
+
readonly controller: CaptainControllerPort;
|
|
17
19
|
}) => PlaybookRuntime;
|
|
18
20
|
}
|
|
19
21
|
export interface PlaybookCaptainRegistryEntry {
|