@sublang/playbook 2.0.0 → 3.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.
Files changed (30) hide show
  1. package/README.md +102 -338
  2. package/docs/cli.md +123 -0
  3. package/docs/configuration.md +158 -0
  4. package/docs/embedding.md +161 -0
  5. package/package.json +7 -2
  6. package/reference/sdlc/code.md +105 -0
  7. package/reference/sdlc/code.playbook/bin/playbook.js +237 -38
  8. package/reference/sdlc/code.playbook/bin/provision.js +228 -0
  9. package/reference/sdlc/code.playbook/bin/run.js +76 -3
  10. package/reference/sdlc/code.playbook/code.fsm.js +38 -36
  11. package/reference/sdlc/code.playbook/code.fsm.ts +38 -36
  12. package/reference/sdlc/code.playbook/code.gears.md +30 -26
  13. package/reference/sdlc/code.playbook/code.playbook.js +4 -0
  14. package/reference/sdlc/code.playbook/code.playbook.ts +6 -0
  15. package/reference/sdlc/code.playbook/playbook-captain.js +67 -8
  16. package/reference/sdlc/code.playbook/playbook-captain.ts +80 -9
  17. package/reference/sdlc/code.playbook/playbook.config.template.yaml +38 -32
  18. package/reference/sdlc/discuss.md +93 -0
  19. package/reference/sdlc/discuss.playbook/discuss.fsm.js +5 -4
  20. package/reference/sdlc/discuss.playbook/discuss.fsm.ts +5 -4
  21. package/reference/sdlc/discuss.playbook/discuss.gears.md +19 -12
  22. package/reference/sdlc/discuss.playbook/discuss.playbook.js +3 -0
  23. package/reference/sdlc/discuss.playbook/discuss.playbook.ts +5 -0
  24. package/slc/link.md +20 -3
  25. package/src/xstate-playbook-runtime.d.ts +17 -0
  26. package/src/xstate-playbook-runtime.js +49 -0
  27. package/src/xstate-playbook-runtime.ts +81 -4
  28. package/src/xstate-runtime.d.ts +1 -0
  29. package/src/xstate-runtime.js +19 -0
  30. package/src/xstate-runtime.ts +20 -0
@@ -15,7 +15,7 @@ Players:
15
15
 
16
16
  When Boss gives a coding intent, Captain shall relay it to Coder along with the following prompt:
17
17
  > Assess whether this can be completed in a single commit, following best practices.
18
- > If yes, implement and test, updating both code and specs; otherwise, decompose into tasks as a new IR under @specs/iterations and stop without implementing any IR task.
18
+ > If yes, implement and test, updating both code and specs; otherwise, decompose into tasks as a new IR under @specs/intents (or @specs/iterations in older scaffolds) and stop without implementing any IR task.
19
19
  > For context discovery, @specs/map.md indexes all spec files and @specs/meta.md describes the spec format.
20
20
  > Ensure @specs/map.md reflects the changes.
21
21
  > Do not commit.
@@ -42,9 +42,9 @@ When an IR is done, Captain shall prompt Coder:
42
42
  > Read IR-<#> and corresponding commits.
43
43
  > According to @specs/meta.md, add or update spec items to fully capture:
44
44
  >
45
- > - the user requirements in @specs/user,
46
- > - the system behavior in @specs/dev, and
47
- > - the integration/system test cases in @specs/test.
45
+ > - the external behavior users rely on,
46
+ > - the internal system behavior, and
47
+ > - the integration/system test cases.
48
48
  >
49
49
  > The spec items should be the *minimal* set needed to reimplement code without the IR.
50
50
  > The set should be complete and coherent.
@@ -58,15 +58,17 @@ For each finding in a review round, Coder either addresses it with changes or ch
58
58
  Any code change to address findings starts a new round of review, even if some findings are also rebutted.
59
59
  Rounds continue until Reviewer raises no findings.
60
60
 
61
+ Spec item files are the files under @specs/ that hold spec items — @specs/packages/ and @specs/compositions/ in the current layout, or @specs/user/, @specs/dev/, and @specs/test/ in the legacy one; decision and intent records (iteration records in older scaffolds), @specs/map.md, and @specs/meta.md are not spec item files.
62
+
61
63
  ### CODE-5
62
64
 
63
- When Committer commits Initial Changes from a Boss coding intent involving changes only in @specs/user/, @specs/dev/, or @specs/test/, Captain shall relay the Boss's coding intent to Reviewer along with the following prompt:
65
+ When Committer commits Initial Changes from a Boss coding intent involving changes only in spec item files, Captain shall relay the Boss's coding intent to Reviewer along with the following prompt:
64
66
  > Review the latest commit.
65
67
  > Refer to the commit message.
66
68
  > Verify any affected spec items are:
67
69
  >
68
70
  > - Complete & coherent: sufficient for you to reimplement code.
69
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
71
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
70
72
  > - Minimal: essential and concise; every item earns its place; also check with other items.
71
73
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
72
74
  >
@@ -78,7 +80,7 @@ When Committer commits Initial Changes from a Boss coding intent involving chang
78
80
 
79
81
  ### CODE-6
80
82
 
81
- When Committer commits Initial Changes from a Boss coding intent involving changes only outside @specs/user/, @specs/dev/, and @specs/test/, Captain shall relay the Boss's coding intent to Reviewer along with the following prompt:
83
+ When Committer commits Initial Changes from a Boss coding intent involving changes only outside spec item files, Captain shall relay the Boss's coding intent to Reviewer along with the following prompt:
82
84
  > Review the latest commit.
83
85
  > Refer to the commit message.
84
86
  > Flag any issues or improvements (numbered; no duplication).
@@ -90,7 +92,7 @@ When Committer commits Initial Changes from a Boss coding intent involving chang
90
92
 
91
93
  ### CODE-7
92
94
 
93
- When Committer commits Initial Changes from a Boss coding intent involving changes both in and outside @specs/user/, @specs/dev/, and @specs/test/, Captain shall relay the Boss's coding intent to Reviewer along with the following prompt:
95
+ When Committer commits Initial Changes from a Boss coding intent involving changes both in and outside spec item files, Captain shall relay the Boss's coding intent to Reviewer along with the following prompt:
94
96
  > Review the latest commit.
95
97
  > Refer to the commit message.
96
98
  > Flag any issues or improvements (numbered; no duplication).
@@ -98,7 +100,7 @@ When Committer commits Initial Changes from a Boss coding intent involving chang
98
100
  > Verify any affected spec items are:
99
101
  >
100
102
  > - Complete & coherent: sufficient for you to reimplement code.
101
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
103
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
102
104
  > - Minimal: essential and concise; every item earns its place; also check with other items.
103
105
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
104
106
  >
@@ -110,13 +112,13 @@ When Committer commits Initial Changes from a Boss coding intent involving chang
110
112
 
111
113
  ### CODE-8
112
114
 
113
- When Committer commits Initial Changes from an IR task involving changes only in @specs/user/, @specs/dev/, or @specs/test/, Captain shall relay the IR's task description to Reviewer along with the following prompt:
115
+ When Committer commits Initial Changes from an IR task involving changes only in spec item files, Captain shall relay the IR's task description to Reviewer along with the following prompt:
114
116
  > Review the latest commit.
115
117
  > Refer to the commit message.
116
118
  > Verify any affected spec items are:
117
119
  >
118
120
  > - Complete & coherent: sufficient for you to reimplement code.
119
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
121
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
120
122
  > - Minimal: essential and concise; every item earns its place; also check with other items.
121
123
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
122
124
  >
@@ -128,7 +130,7 @@ When Committer commits Initial Changes from an IR task involving changes only in
128
130
 
129
131
  ### CODE-9
130
132
 
131
- When Committer commits Initial Changes from an IR task involving changes only outside @specs/user/, @specs/dev/, and @specs/test/, Captain shall relay the IR's task description to Reviewer along with the following prompt:
133
+ When Committer commits Initial Changes from an IR task involving changes only outside spec item files, Captain shall relay the IR's task description to Reviewer along with the following prompt:
132
134
  > Review the latest commit.
133
135
  > Refer to the commit message.
134
136
  > Flag any issues or improvements (numbered; no duplication).
@@ -140,7 +142,7 @@ When Committer commits Initial Changes from an IR task involving changes only ou
140
142
 
141
143
  ### CODE-10
142
144
 
143
- When Committer commits Initial Changes from an IR task involving changes both in and outside @specs/user/, @specs/dev/, and @specs/test/, Captain shall relay the IR's task description to Reviewer along with the following prompt:
145
+ When Committer commits Initial Changes from an IR task involving changes both in and outside spec item files, Captain shall relay the IR's task description to Reviewer along with the following prompt:
144
146
  > Review the latest commit.
145
147
  > Refer to the commit message.
146
148
  > Flag any issues or improvements (numbered; no duplication).
@@ -148,7 +150,7 @@ When Committer commits Initial Changes from an IR task involving changes both in
148
150
  > Verify any affected spec items are:
149
151
  >
150
152
  > - Complete & coherent: sufficient for you to reimplement code.
151
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
153
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
152
154
  > - Minimal: essential and concise; every item earns its place; also check with other items.
153
155
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
154
156
  >
@@ -160,13 +162,13 @@ When Committer commits Initial Changes from an IR task involving changes both in
160
162
 
161
163
  ### CODE-11
162
164
 
163
- When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only in @specs/user/, @specs/dev/, or @specs/test/ without raising rebuttals, Captain shall prompt Reviewer to begin a review round:
165
+ When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only in spec item files without raising rebuttals, Captain shall prompt Reviewer to begin a review round:
164
166
  > Review the unstaged and untracked changes in the context of the staged changes.
165
167
  > Understand the intent.
166
168
  > Verify any affected spec items are:
167
169
  >
168
170
  > - Complete & coherent: sufficient for you to reimplement code.
169
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
171
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
170
172
  > - Minimal: essential and concise; every item earns its place; also check with other items.
171
173
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
172
174
  >
@@ -178,7 +180,7 @@ When Coder makes unreviewed changes (outside of any Initial Changes) involving c
178
180
 
179
181
  ### CODE-12
180
182
 
181
- When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only outside @specs/user/, @specs/dev/, and @specs/test/ without raising rebuttals, Captain shall prompt Reviewer to begin a review round:
183
+ When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only outside spec item files without raising rebuttals, Captain shall prompt Reviewer to begin a review round:
182
184
  > Review the unstaged and untracked changes in the context of the staged changes.
183
185
  > Understand the intent.
184
186
  > Flag any issues or improvements (numbered; no duplication).
@@ -190,7 +192,7 @@ When Coder makes unreviewed changes (outside of any Initial Changes) involving c
190
192
 
191
193
  ### CODE-13
192
194
 
193
- When Coder makes unreviewed changes (outside of any Initial Changes) involving changes both in and outside @specs/user/, @specs/dev/, and @specs/test/ without raising rebuttals, Captain shall prompt Reviewer to begin a review round:
195
+ When Coder makes unreviewed changes (outside of any Initial Changes) involving changes both in and outside spec item files without raising rebuttals, Captain shall prompt Reviewer to begin a review round:
194
196
  > Review the unstaged and untracked changes in the context of the staged changes.
195
197
  > Understand the intent.
196
198
  > Flag any issues or improvements (numbered; no duplication).
@@ -198,7 +200,7 @@ When Coder makes unreviewed changes (outside of any Initial Changes) involving c
198
200
  > Verify any affected spec items are:
199
201
  >
200
202
  > - Complete & coherent: sufficient for you to reimplement code.
201
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
203
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
202
204
  > - Minimal: essential and concise; every item earns its place; also check with other items.
203
205
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
204
206
  >
@@ -216,13 +218,13 @@ When Coder raises rebuttals without making code changes, Captain shall relay the
216
218
 
217
219
  ### CODE-15
218
220
 
219
- When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only in @specs/user/, @specs/dev/, or @specs/test/ and also raises rebuttals, Captain shall prompt Reviewer to begin a review round and relay the rebuttals along with the following prompt:
221
+ When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only in spec item files and also raises rebuttals, Captain shall prompt Reviewer to begin a review round and relay the rebuttals along with the following prompt:
220
222
  > Review the unstaged and untracked changes in the context of the staged changes.
221
223
  > Understand the intent.
222
224
  > Verify any affected spec items are:
223
225
  >
224
226
  > - Complete & coherent: sufficient for you to reimplement code.
225
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
227
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
226
228
  > - Minimal: essential and concise; every item earns its place; also check with other items.
227
229
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
228
230
  >
@@ -235,7 +237,7 @@ When Coder makes unreviewed changes (outside of any Initial Changes) involving c
235
237
 
236
238
  ### CODE-16
237
239
 
238
- When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only outside @specs/user/, @specs/dev/, and @specs/test/ and also raises rebuttals, Captain shall prompt Reviewer to begin a review round and relay the rebuttals along with the following prompt:
240
+ When Coder makes unreviewed changes (outside of any Initial Changes) involving changes only outside spec item files and also raises rebuttals, Captain shall prompt Reviewer to begin a review round and relay the rebuttals along with the following prompt:
239
241
  > Review the unstaged and untracked changes in the context of the staged changes.
240
242
  > Understand the intent.
241
243
  > Flag any issues or improvements (numbered; no duplication).
@@ -248,7 +250,7 @@ When Coder makes unreviewed changes (outside of any Initial Changes) involving c
248
250
 
249
251
  ### CODE-17
250
252
 
251
- When Coder makes unreviewed changes (outside of any Initial Changes) involving changes both in and outside @specs/user/, @specs/dev/, and @specs/test/ and also raises rebuttals, Captain shall prompt Reviewer to begin a review round and relay the rebuttals along with the following prompt:
253
+ When Coder makes unreviewed changes (outside of any Initial Changes) involving changes both in and outside spec item files and also raises rebuttals, Captain shall prompt Reviewer to begin a review round and relay the rebuttals along with the following prompt:
252
254
  > Review the unstaged and untracked changes in the context of the staged changes.
253
255
  > Understand the intent.
254
256
  > Flag any issues or improvements (numbered; no duplication).
@@ -256,7 +258,7 @@ When Coder makes unreviewed changes (outside of any Initial Changes) involving c
256
258
  > Verify any affected spec items are:
257
259
  >
258
260
  > - Complete & coherent: sufficient for you to reimplement code.
259
- > - Right level: user requirements (in @specs/user) or system behavior (in @specs/dev), not implementation specifics; integration/system testing (in @specs/test), not unit testing.
261
+ > - Right level: external behavior users rely on or internal system behavior (organized per @specs/meta.md), not implementation specifics; integration/system testing, not unit testing.
260
262
  > - Minimal: essential and concise; every item earns its place; also check with other items.
261
263
  > - Well organized: spec packages are finely scoped, with high cohesion and low coupling.
262
264
  >
@@ -272,7 +274,8 @@ When Coder makes unreviewed changes (outside of any Initial Changes) involving c
272
274
  ### CODE-18
273
275
 
274
276
  When Coder makes any Initial Changes and Reviewer has not played since the last commit, Captain shall prompt Committer:
275
- > Make a commit of the changes that belong in the repo, following @specs/dev/git.md (reread if necessary).
277
+ > Make a commit of the changes that belong in the repo, following @specs/packages/git.md (reread if necessary).
278
+ > If that spec is absent, follow the legacy @specs/dev/git.md; if neither exists, follow the repository's existing commit conventions and do not search elsewhere.
276
279
  > Write the commit message concisely.
277
280
  > Coder is <coder-llm>.
278
281
  > Format the `Co-authored-by` `<model>` token as the conventional human form of the substituted id (e.g., `claude-opus-4-7` → `Claude-Opus-4.7`, `gpt-5.5` → `GPT-5.5`).
@@ -282,7 +285,8 @@ Result guard: `needsBossInput` — Committing requires additional Boss input or
282
285
  ### CODE-19
283
286
 
284
287
  When Coder makes any Initial Changes and Reviewer has played since the last commit, or Reviewer raises no findings on uncommitted changes and Coder has played since the last commit, Captain shall prompt Committer:
285
- > Make a commit of the changes that belong in the repo, following @specs/dev/git.md (reread if necessary).
288
+ > Make a commit of the changes that belong in the repo, following @specs/packages/git.md (reread if necessary).
289
+ > If that spec is absent, follow the legacy @specs/dev/git.md; if neither exists, follow the repository's existing commit conventions and do not search elsewhere.
286
290
  > Write the commit message concisely.
287
291
  > Coder is <coder-llm>; Reviewer is <reviewer-llm>.
288
292
  > Format the `Co-authored-by` `<model>` token as the conventional human form of the substituted id (e.g., `claude-opus-4-7` → `Claude-Opus-4.7`, `gpt-5.5` → `GPT-5.5`).
@@ -189,6 +189,10 @@ function extractRequiredFields(description) {
189
189
  }
190
190
  function buildJudgePrompt(input, finalText) {
191
191
  const lines = [];
192
+ lines.push('This is hidden control work. Do not call tools, inspect files, or ' +
193
+ 'seek external evidence. Decide only from the supplied player output ' +
194
+ 'and outcome descriptions. Reply with exactly one JSON object and no prose.');
195
+ lines.push('');
192
196
  lines.push(`The ${input.player} just produced this output:`);
193
197
  lines.push('');
194
198
  lines.push('```');
@@ -290,6 +290,12 @@ function extractRequiredFields(description: string): string[] {
290
290
 
291
291
  function buildJudgePrompt(input: PlayerInput, finalText: string): string {
292
292
  const lines: string[] = [];
293
+ lines.push(
294
+ 'This is hidden control work. Do not call tools, inspect files, or ' +
295
+ 'seek external evidence. Decide only from the supplied player output ' +
296
+ 'and outcome descriptions. Reply with exactly one JSON object and no prose.',
297
+ );
298
+ lines.push('');
293
299
  lines.push(`The ${input.player} just produced this output:`);
294
300
  lines.push('');
295
301
  lines.push('```');
@@ -2,7 +2,7 @@
2
2
  // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import PQueue from 'p-queue';
5
- import { registerPlaybookAbortCleanup } from '../../../src/xstate-runtime.js';
5
+ import { hiddenControlEnvelope, registerPlaybookAbortCleanup, } from '../../../src/xstate-runtime.js';
6
6
  import createDefaultCaptainRuntime from '../captain.playbook/captain.playbook.js';
7
7
  class VisibilityControlError extends Error {
8
8
  constructor(cause) {
@@ -27,6 +27,47 @@ function visibleChatEnvelope(message) {
27
27
  message,
28
28
  ].join('\n\n');
29
29
  }
30
+ // DR-013 A1: adapters with no provider-enforced tool-restriction surface.
31
+ // Cligent's Codex adapter rejects any `allowedTools` value — including the
32
+ // empty list that expresses tool-free — because the supported Codex SDK
33
+ // cannot enforce one, so requesting it fails every control call before the
34
+ // model is reached. Omitting the option is the only way such an adapter can
35
+ // run a control call at all; its isolation then rests on the authored
36
+ // hidden-judge envelope below rather than on provider enforcement.
37
+ const ADAPTERS_WITHOUT_TOOL_ENFORCEMENT = new Set([
38
+ 'codex',
39
+ ]);
40
+ // The tool half of a control call's options. An empty allowlist means "no
41
+ // tools available" and is distinct from omission, which grants the adapter's
42
+ // full native tool surface — so omit only where the empty list would be
43
+ // refused, and keep requesting enforcement whenever the adapter is unknown.
44
+ function controlCallToolOptions(captainAdapter) {
45
+ if (captainAdapter !== undefined &&
46
+ ADAPTERS_WITHOUT_TOOL_ENFORCEMENT.has(captainAdapter)) {
47
+ return {};
48
+ }
49
+ return { allowedTools: [] };
50
+ }
51
+ // A runtime-requested allowlist forwarded to the captain agent. The empty
52
+ // list is the runtime's way of saying "tool-free", so it is the only value
53
+ // the host substitutes; a non-empty list is a real restriction and stays
54
+ // fail-closed on an adapter that cannot enforce it.
55
+ function forwardedToolOptions(requested, captainAdapter) {
56
+ if (requested === undefined)
57
+ return {};
58
+ if (requested.length === 0)
59
+ return controlCallToolOptions(captainAdapter);
60
+ return { allowedTools: requested };
61
+ }
62
+ function readCaptainAdapter(options) {
63
+ if (typeof options !== 'object' || options === null)
64
+ return undefined;
65
+ const adapter = options.captainAdapter;
66
+ return typeof adapter === 'string' && adapter.length > 0
67
+ ? adapter
68
+ : undefined;
69
+ }
70
+ const hiddenJudgeEnvelope = hiddenControlEnvelope;
30
71
  function visibleTurnSummaryEnvelope(input) {
31
72
  return [
32
73
  'You are the Playbook Captain shell.',
@@ -176,6 +217,10 @@ export function createPlaybookCaptainShell(options, deps = {}) {
176
217
  const loadModule = deps.loadModule ?? ((specifier) => import(specifier));
177
218
  const createSessionId = deps.createSessionId ?? randomUUID;
178
219
  const createCaptainRuntime = deps.createCaptainRuntime ?? createDefaultCaptainRuntime;
220
+ // DR-013 A1: the launcher passes the resolved captain adapter through
221
+ // `captain.options`; a raw `--config` launch leaves it undefined, which
222
+ // keeps the enforced empty allowlist and its fail-closed behavior.
223
+ const captainAdapter = readCaptainAdapter(options);
179
224
  let entries = [];
180
225
  let byCommand = new Map();
181
226
  let byId = new Map();
@@ -382,9 +427,7 @@ export function createPlaybookCaptainShell(options, deps = {}) {
382
427
  const result = await callCaptainQueued(frame, activeContext, prompt, {
383
428
  visibility: options.visibility,
384
429
  resume: options.resume,
385
- ...(options.allowedTools === undefined
386
- ? {}
387
- : { allowedTools: options.allowedTools }),
430
+ ...forwardedToolOptions(options.allowedTools, captainAdapter),
388
431
  }, signal);
389
432
  return {
390
433
  status: result.status,
@@ -398,7 +441,11 @@ export function createPlaybookCaptainShell(options, deps = {}) {
398
441
  if (!activeContext) {
399
442
  throw new Error('callJudge invoked outside a Boss turn');
400
443
  }
401
- const result = await callCaptainQueued(frame, activeContext, prompt, { visibility: 'hidden', resume: false, allowedTools: [] }, signal);
444
+ const result = await callCaptainQueued(frame, activeContext, hiddenJudgeEnvelope(prompt), {
445
+ visibility: 'hidden',
446
+ resume: false,
447
+ ...controlCallToolOptions(captainAdapter),
448
+ }, signal);
402
449
  if (result.status !== 'ok') {
403
450
  throw new Error(result.error ?? `callCaptain status "${result.status}"`);
404
451
  }
@@ -1101,13 +1148,21 @@ export function createPlaybookCaptainShell(options, deps = {}) {
1101
1148
  }
1102
1149
  };
1103
1150
  const callVisibleChat = async (frame, context, message) => {
1104
- const result = await callCaptainQueued(frame, context, visibleChatEnvelope(message), { visibility: 'visible', resume: false, allowedTools: [] }, context.signal);
1151
+ const result = await callCaptainQueued(frame, context, visibleChatEnvelope(message), {
1152
+ visibility: 'visible',
1153
+ resume: false,
1154
+ ...controlCallToolOptions(captainAdapter),
1155
+ }, context.signal);
1105
1156
  if (result.status !== 'ok') {
1106
1157
  throw new Error(result.error ?? `callCaptain status "${result.status}"`);
1107
1158
  }
1108
1159
  };
1109
1160
  const callVisibleTurnSummary = async (frame, context, input) => {
1110
- const result = await callCaptainQueued(frame, context, visibleTurnSummaryEnvelope(input), { visibility: 'visible', resume: false, allowedTools: [] }, context.signal);
1161
+ const result = await callCaptainQueued(frame, context, visibleTurnSummaryEnvelope(input), {
1162
+ visibility: 'visible',
1163
+ resume: false,
1164
+ ...controlCallToolOptions(captainAdapter),
1165
+ }, context.signal);
1111
1166
  if (result.status !== 'ok') {
1112
1167
  throw new Error(result.error ?? `callCaptain status "${result.status}"`);
1113
1168
  }
@@ -1151,7 +1206,11 @@ export function createPlaybookCaptainShell(options, deps = {}) {
1151
1206
  }
1152
1207
  let decision;
1153
1208
  try {
1154
- const result = await callCaptainQueued(leaf, context, hiddenLifecycleEnvelope(turn.prompt), { visibility: 'hidden', resume: false, allowedTools: [] }, context.signal);
1209
+ const result = await callCaptainQueued(leaf, context, hiddenLifecycleEnvelope(turn.prompt), {
1210
+ visibility: 'hidden',
1211
+ resume: false,
1212
+ ...controlCallToolOptions(captainAdapter),
1213
+ }, context.signal);
1155
1214
  if (result.status === 'ok' && result.finalText !== undefined) {
1156
1215
  decision = parseLifecycleDecision(result.finalText);
1157
1216
  }
@@ -20,7 +20,10 @@ import type {
20
20
  PlaybookRuntime,
21
21
  PlaybookState,
22
22
  } from '@sublang/playbook/runtime';
23
- import { registerPlaybookAbortCleanup } from '../../../src/xstate-runtime.js';
23
+ import {
24
+ hiddenControlEnvelope,
25
+ registerPlaybookAbortCleanup,
26
+ } from '../../../src/xstate-runtime.js';
24
27
  import createDefaultCaptainRuntime from '../captain.playbook/captain.playbook.js';
25
28
  import type { PlaybookSummaryPolicy, RegistryPlayer } from './code.registry.js';
26
29
 
@@ -154,6 +157,56 @@ function visibleChatEnvelope(message: string): string {
154
157
  ].join('\n\n');
155
158
  }
156
159
 
160
+ // DR-013 A1: adapters with no provider-enforced tool-restriction surface.
161
+ // Cligent's Codex adapter rejects any `allowedTools` value — including the
162
+ // empty list that expresses tool-free — because the supported Codex SDK
163
+ // cannot enforce one, so requesting it fails every control call before the
164
+ // model is reached. Omitting the option is the only way such an adapter can
165
+ // run a control call at all; its isolation then rests on the authored
166
+ // hidden-judge envelope below rather than on provider enforcement.
167
+ const ADAPTERS_WITHOUT_TOOL_ENFORCEMENT: ReadonlySet<string> = new Set([
168
+ 'codex',
169
+ ]);
170
+
171
+ // The tool half of a control call's options. An empty allowlist means "no
172
+ // tools available" and is distinct from omission, which grants the adapter's
173
+ // full native tool surface — so omit only where the empty list would be
174
+ // refused, and keep requesting enforcement whenever the adapter is unknown.
175
+ function controlCallToolOptions(
176
+ captainAdapter: string | undefined,
177
+ ): { allowedTools?: readonly string[] } {
178
+ if (
179
+ captainAdapter !== undefined &&
180
+ ADAPTERS_WITHOUT_TOOL_ENFORCEMENT.has(captainAdapter)
181
+ ) {
182
+ return {};
183
+ }
184
+ return { allowedTools: [] };
185
+ }
186
+
187
+ // A runtime-requested allowlist forwarded to the captain agent. The empty
188
+ // list is the runtime's way of saying "tool-free", so it is the only value
189
+ // the host substitutes; a non-empty list is a real restriction and stays
190
+ // fail-closed on an adapter that cannot enforce it.
191
+ function forwardedToolOptions(
192
+ requested: readonly string[] | undefined,
193
+ captainAdapter: string | undefined,
194
+ ): { allowedTools?: readonly string[] } {
195
+ if (requested === undefined) return {};
196
+ if (requested.length === 0) return controlCallToolOptions(captainAdapter);
197
+ return { allowedTools: requested };
198
+ }
199
+
200
+ function readCaptainAdapter(options: unknown): string | undefined {
201
+ if (typeof options !== 'object' || options === null) return undefined;
202
+ const adapter = (options as Record<string, unknown>).captainAdapter;
203
+ return typeof adapter === 'string' && adapter.length > 0
204
+ ? adapter
205
+ : undefined;
206
+ }
207
+
208
+ const hiddenJudgeEnvelope = hiddenControlEnvelope;
209
+
157
210
  function visibleTurnSummaryEnvelope(input: {
158
211
  playbookId: string;
159
212
  submittedText: string;
@@ -368,6 +421,10 @@ export function createPlaybookCaptainShell(
368
421
  const createCaptainRuntime: NonNullable<
369
422
  PlaybookCaptainDeps['createCaptainRuntime']
370
423
  > = deps.createCaptainRuntime ?? createDefaultCaptainRuntime;
424
+ // DR-013 A1: the launcher passes the resolved captain adapter through
425
+ // `captain.options`; a raw `--config` launch leaves it undefined, which
426
+ // keeps the enforced empty allowlist and its fail-closed behavior.
427
+ const captainAdapter = readCaptainAdapter(options);
371
428
  let entries: readonly PlaybookCaptainRegistryEntry[] = [];
372
429
  let byCommand = new Map<string, PlaybookCaptainRegistryEntry>();
373
430
  let byId = new Map<string, PlaybookCaptainRegistryEntry>();
@@ -650,9 +707,7 @@ export function createPlaybookCaptainShell(
650
707
  {
651
708
  visibility: options.visibility,
652
709
  resume: options.resume,
653
- ...(options.allowedTools === undefined
654
- ? {}
655
- : { allowedTools: options.allowedTools }),
710
+ ...forwardedToolOptions(options.allowedTools, captainAdapter),
656
711
  },
657
712
  signal,
658
713
  );
@@ -671,8 +726,12 @@ export function createPlaybookCaptainShell(
671
726
  const result = await callCaptainQueued(
672
727
  frame,
673
728
  activeContext,
674
- prompt,
675
- { visibility: 'hidden', resume: false, allowedTools: [] },
729
+ hiddenJudgeEnvelope(prompt),
730
+ {
731
+ visibility: 'hidden',
732
+ resume: false,
733
+ ...controlCallToolOptions(captainAdapter),
734
+ },
676
735
  signal,
677
736
  );
678
737
  if (result.status !== 'ok') {
@@ -1495,7 +1554,11 @@ export function createPlaybookCaptainShell(
1495
1554
  frame,
1496
1555
  context,
1497
1556
  visibleChatEnvelope(message),
1498
- { visibility: 'visible', resume: false, allowedTools: [] },
1557
+ {
1558
+ visibility: 'visible',
1559
+ resume: false,
1560
+ ...controlCallToolOptions(captainAdapter),
1561
+ },
1499
1562
  context.signal,
1500
1563
  );
1501
1564
  if (result.status !== 'ok') {
@@ -1519,7 +1582,11 @@ export function createPlaybookCaptainShell(
1519
1582
  frame,
1520
1583
  context,
1521
1584
  visibleTurnSummaryEnvelope(input),
1522
- { visibility: 'visible', resume: false, allowedTools: [] },
1585
+ {
1586
+ visibility: 'visible',
1587
+ resume: false,
1588
+ ...controlCallToolOptions(captainAdapter),
1589
+ },
1523
1590
  context.signal,
1524
1591
  );
1525
1592
  if (result.status !== 'ok') {
@@ -1577,7 +1644,11 @@ export function createPlaybookCaptainShell(
1577
1644
  leaf,
1578
1645
  context,
1579
1646
  hiddenLifecycleEnvelope(turn.prompt),
1580
- { visibility: 'hidden', resume: false, allowedTools: [] },
1647
+ {
1648
+ visibility: 'hidden',
1649
+ resume: false,
1650
+ ...controlCallToolOptions(captainAdapter),
1651
+ },
1581
1652
  context.signal,
1582
1653
  );
1583
1654
  if (result.status === 'ok' && result.finalText !== undefined) {
@@ -6,39 +6,25 @@
6
6
  # The launcher injects captain.from and the namespaced <id>-<role> host
7
7
  # players, then launches cligent's tmux-play under the Playbook Captain shell.
8
8
 
9
- # Reusable agent settings. A profile id must not be an adapter shorthand
10
- # (claude, codex). Reference a profile by id from captain / players below.
11
- # Profile ids name the underlying agent/model (e.g. claude-opus,
12
- # codex-gpt) so they read distinctly from the captain / coder / reviewer
13
- # player roles that reference them.
9
+ # Every agent the Captain and each playbook role carries its own
10
+ # settings inline: an adapter shorthand (claude, codex) or a block with
11
+ # adapter/model/effort/permissions. Retuning one agent never changes
12
+ # another.
14
13
  # Every seeded agent runs in cligent's protected auto mode
15
14
  # (permissions.mode: auto): claude maps it to permissionMode auto, codex to
16
15
  # on-request + auto_review. Codex roles also grant writablePaths: ['.git']
17
16
  # so commit turns can write git metadata under the codex sandbox.
18
- profiles:
19
- claude-opus:
20
- adapter: claude
21
- model: claude-opus-4-8
22
- reasoningEffort: high
23
- permissions:
24
- mode: auto
25
- claude-opus-1m:
26
- adapter: claude
27
- model: claude-opus-4-8[1m]
28
- reasoningEffort: xhigh
29
- permissions:
30
- mode: auto
31
- codex-gpt:
32
- adapter: codex
33
- model: gpt-5.5
34
- reasoningEffort: xhigh
35
- permissions:
36
- mode: auto
37
- writablePaths: ['.git']
38
17
 
39
- # The Captain/Judge agent: a profile id, an adapter shorthand, or a full
40
- # tmux-play agent block (which may carry a `profile:` key).
41
- captain: claude-opus
18
+ # The Captain/Judge agent.
19
+ # Captain routing/adjudication calls run tool-free. Claude enforces that at
20
+ # the provider level; the codex adapter cannot enforce a tool list, so a
21
+ # codex captain degrades to a prompt-level restriction (DR-013 A1).
22
+ captain:
23
+ adapter: claude
24
+ model: claude-opus-4-8
25
+ effort: high
26
+ permissions:
27
+ mode: auto
42
28
 
43
29
  # Host notifications. Omitting turn_aborted resolves it to off.
44
30
  notifications:
@@ -53,8 +39,19 @@ playbooks:
53
39
  code:
54
40
  from: "@sublang/playbook/code/registry"
55
41
  players:
56
- coder: claude-opus-1m
57
- reviewer: codex-gpt
42
+ coder:
43
+ adapter: claude
44
+ model: claude-opus-4-8[1m]
45
+ effort: xhigh
46
+ permissions:
47
+ mode: auto
48
+ reviewer:
49
+ adapter: codex
50
+ model: gpt-5.5
51
+ effort: xhigh
52
+ permissions:
53
+ mode: auto
54
+ writablePaths: ['.git']
58
55
  committer: coder
59
56
 
60
57
  # The DISCUSS playbook: two agents converge on spec items or decision
@@ -63,8 +60,17 @@ playbooks:
63
60
  # discuss:
64
61
  # from: "@sublang/playbook/discuss/registry"
65
62
  # players:
66
- # host: claude-opus
67
- # participant: codex-gpt
63
+ # host:
64
+ # adapter: claude
65
+ # model: claude-opus-4-8
66
+ # permissions:
67
+ # mode: auto
68
+ # participant:
69
+ # adapter: codex
70
+ # model: gpt-5.5
71
+ # permissions:
72
+ # mode: auto
73
+ # writablePaths: ['.git']
68
74
  # committer: host
69
75
 
70
76
  # Non-interactive `playbook run` defaults (optional). Each value is an