@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.
Files changed (73) hide show
  1. package/README.md +69 -122
  2. package/docs/assets/playbook-venn.svg +13 -0
  3. package/docs/cli.md +43 -26
  4. package/docs/configuration.md +63 -18
  5. package/docs/embedding.md +24 -16
  6. package/package.json +43 -22
  7. package/reference/sdlc/captain.md +70 -83
  8. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +127 -142
  9. package/reference/sdlc/captain.playbook/captain.fsm.js +349 -470
  10. package/reference/sdlc/captain.playbook/captain.fsm.ts +535 -598
  11. package/reference/sdlc/captain.playbook/captain.gears.md +37 -41
  12. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +90 -15
  13. package/reference/sdlc/captain.playbook/captain.playbook.js +466 -976
  14. package/reference/sdlc/captain.playbook/captain.playbook.ts +698 -1001
  15. package/reference/sdlc/code.md +55 -97
  16. package/reference/sdlc/code.playbook/code.fsm.d.ts +229 -94
  17. package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +26 -44
  18. package/reference/sdlc/code.playbook/code.fsm.introspect.js +61 -66
  19. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +100 -149
  20. package/reference/sdlc/code.playbook/code.fsm.js +587 -1347
  21. package/reference/sdlc/code.playbook/code.fsm.ts +809 -1650
  22. package/reference/sdlc/code.playbook/code.gears.md +51 -263
  23. package/reference/sdlc/code.playbook/code.playbook.d.ts +8 -47
  24. package/reference/sdlc/code.playbook/code.playbook.js +69 -639
  25. package/reference/sdlc/code.playbook/code.playbook.ts +90 -850
  26. package/reference/sdlc/code.playbook/code.registry.d.ts +9 -25
  27. package/reference/sdlc/code.playbook/code.registry.js +20 -78
  28. package/reference/sdlc/code.playbook/code.registry.ts +58 -122
  29. package/reference/sdlc/code.playbook/playbook-captain.d.ts +2 -0
  30. package/reference/sdlc/code.playbook/playbook-captain.js +1877 -251
  31. package/reference/sdlc/code.playbook/playbook-captain.ts +2385 -352
  32. package/reference/sdlc/code.playbook/playbook.config.template.yaml +33 -22
  33. package/reference/sdlc/decide.md +54 -0
  34. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +261 -0
  35. package/reference/sdlc/decide.playbook/decide.fsm.js +894 -0
  36. package/reference/sdlc/decide.playbook/decide.fsm.ts +1152 -0
  37. package/reference/sdlc/decide.playbook/decide.gears.md +88 -0
  38. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +67 -0
  39. package/reference/sdlc/{discuss.playbook/discuss.playbook.js → decide.playbook/decide.playbook.js} +511 -370
  40. package/reference/sdlc/{discuss.playbook/discuss.playbook.ts → decide.playbook/decide.playbook.ts} +616 -451
  41. package/reference/sdlc/decide.playbook/decide.registry.d.ts +41 -0
  42. package/reference/sdlc/decide.playbook/decide.registry.js +60 -0
  43. package/reference/sdlc/decide.playbook/decide.registry.ts +125 -0
  44. package/reference/sdlc/review.md +81 -0
  45. package/reference/sdlc/review.playbook/review.fsm.d.ts +183 -0
  46. package/reference/sdlc/review.playbook/review.fsm.js +524 -0
  47. package/reference/sdlc/review.playbook/review.fsm.ts +652 -0
  48. package/reference/sdlc/review.playbook/review.gears.md +112 -0
  49. package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -0
  50. package/reference/sdlc/review.playbook/review.playbook.js +112 -0
  51. package/reference/sdlc/review.playbook/review.playbook.ts +201 -0
  52. package/reference/sdlc/review.playbook/review.registry.d.ts +43 -0
  53. package/reference/sdlc/review.playbook/review.registry.js +73 -0
  54. package/reference/sdlc/review.playbook/review.registry.ts +138 -0
  55. package/slc/gears2fsm.md +67 -6
  56. package/slc/link.md +339 -25
  57. package/slc/text2gears.md +22 -2
  58. package/src/runtime.d.ts +36 -1
  59. package/src/runtime.ts +59 -0
  60. package/src/xstate-playbook-runtime.d.ts +96 -7
  61. package/src/xstate-playbook-runtime.js +1018 -49
  62. package/src/xstate-playbook-runtime.ts +1283 -59
  63. package/src/xstate-runtime.js +25 -0
  64. package/src/xstate-runtime.ts +51 -0
  65. package/reference/sdlc/discuss.md +0 -93
  66. package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +0 -396
  67. package/reference/sdlc/discuss.playbook/discuss.fsm.js +0 -2067
  68. package/reference/sdlc/discuss.playbook/discuss.fsm.ts +0 -2465
  69. package/reference/sdlc/discuss.playbook/discuss.gears.md +0 -258
  70. package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +0 -113
  71. package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +0 -58
  72. package/reference/sdlc/discuss.playbook/discuss.registry.js +0 -97
  73. 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 string[];
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
- // PBRT-30: the CODE registry entry validates the option slice the shell
5
- // passes it (`captain.options.playbooks.code.options`), not a namespace
6
- // it extracts from the full Captain options bag. The schema defines one
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
- 'accepted',
17
- 'approved',
18
- 'challengeAccepted',
19
- 'challengeRejected',
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 (typeof optionSlice !== 'object' ||
73
- optionSlice === null ||
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 slice = optionSlice;
78
- for (const key of Object.keys(slice)) {
79
- if (!CODE_OPTION_KEYS.has(key)) {
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 options;
44
+ return Object.freeze({});
93
45
  }
94
46
  function playerIdentity(players, id) {
95
- const entry = players.find((p) => p.id === id);
96
- return entry?.model ?? entry?.adapter;
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
- const codeOptions = validateCodeOptions(captainOptions);
51
+ validateCodeOptions(captainOptions);
100
52
  const coderPlayer = playerIdentity(players, 'coder');
101
- const reviewerPlayer = playerIdentity(players, 'reviewer');
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: 'software development / SDLC coding workflow',
114
- requiredRoleIds: ['coder', 'reviewer'],
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 string[];
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 slice = optionSlice as Record<string, unknown>;
133
- for (const key of Object.keys(slice)) {
134
- if (!CODE_OPTION_KEYS.has(key)) {
135
- throw new Error(
136
- `Unknown config field captain.options.playbooks.code.options.${key}`,
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 options;
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 entry = players.find((p) => p.id === id);
159
- return entry?.model ?? entry?.adapter;
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
- const codeOptions = validateCodeOptions(captainOptions);
111
+ validateCodeOptions(captainOptions);
167
112
  const coderPlayer = playerIdentity(players, 'coder');
168
- const reviewerPlayer = playerIdentity(players, 'reviewer');
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: 'software development / SDLC coding workflow',
182
- requiredRoleIds: ['coder', 'reviewer'],
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 {