@sublang/playbook 0.2.0 → 0.4.1
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 +85 -16
- package/package.json +3 -2
- package/reference/sdlc/code.playbook/bin/playbook-code.js +271 -34
- package/reference/sdlc/code.playbook/code.fsm.js +15 -13
- package/reference/sdlc/code.playbook/code.fsm.ts +15 -13
- package/reference/sdlc/code.playbook/code.gears.md +15 -13
- package/reference/sdlc/code.playbook/code.playbook.d.ts +3 -5
- package/reference/sdlc/code.playbook/code.playbook.js +69 -63
- package/reference/sdlc/code.playbook/code.playbook.ts +70 -67
- package/reference/sdlc/code.playbook/code.tmux-play.js +30 -10
- package/reference/sdlc/code.playbook/code.tmux-play.ts +30 -13
- package/reference/sdlc/code.playbook/playbook-code.config.template.yaml +47 -0
- package/reference/sdlc/code.playbook/tmux-play.config.yaml +15 -11
- package/reference/sdlc/code.playbook/tmux-play.production.config.yaml +7 -5
|
@@ -65,10 +65,15 @@ const BOSS_REPLY_ERRORS = {
|
|
|
65
65
|
|
|
66
66
|
// Player-prompt composer — DR-004 §6.
|
|
67
67
|
// Substitutes the three placeholder tokens in `input.prompt` (literal
|
|
68
|
-
// string replace, no escaping) and
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
68
|
+
// string replace, no escaping) and arranges labelled blocks around
|
|
69
|
+
// the prompt body. Context blocks the body refers to as prior
|
|
70
|
+
// material (`Boss intent:`, `Task description:`) are prepended;
|
|
71
|
+
// action blocks the body refers to as material below
|
|
72
|
+
// (`Review items:`, `Rebuttals:`) are appended after the body so the
|
|
73
|
+
// CODE-N prompts' "review item below" / "rebuttal below" phrasing
|
|
74
|
+
// matches the rendered layout. When a state resumes from a Boss
|
|
75
|
+
// reply, the continuation preamble and Q/A blocks precede every
|
|
76
|
+
// ordinary block. The FSM's prompt body is never re-flowed.
|
|
72
77
|
|
|
73
78
|
function composePlayerPrompt(input: CaptainInput): string {
|
|
74
79
|
const blocks: string[] = [];
|
|
@@ -85,12 +90,6 @@ function composePlayerPrompt(input: CaptainInput): string {
|
|
|
85
90
|
if (input.intent !== undefined) {
|
|
86
91
|
blocks.push(`Boss intent:\n${input.intent}`);
|
|
87
92
|
}
|
|
88
|
-
if (input.reviews !== undefined) {
|
|
89
|
-
blocks.push(`Review items:\n${input.reviews}`);
|
|
90
|
-
}
|
|
91
|
-
if (input.challenges !== undefined) {
|
|
92
|
-
blocks.push(`Rebuttals:\n${input.challenges}`);
|
|
93
|
-
}
|
|
94
93
|
if (input.taskDescription !== undefined) {
|
|
95
94
|
blocks.push(`Task description:\n${input.taskDescription}`);
|
|
96
95
|
}
|
|
@@ -107,6 +106,14 @@ function composePlayerPrompt(input: CaptainInput): string {
|
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
blocks.push(body);
|
|
109
|
+
|
|
110
|
+
if (input.reviews !== undefined) {
|
|
111
|
+
blocks.push(`Review items:\n${input.reviews}`);
|
|
112
|
+
}
|
|
113
|
+
if (input.challenges !== undefined) {
|
|
114
|
+
blocks.push(`Rebuttals:\n${input.challenges}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
110
117
|
return blocks.join('\n\n');
|
|
111
118
|
}
|
|
112
119
|
|
|
@@ -478,14 +485,23 @@ function captainBridge(
|
|
|
478
485
|
}
|
|
479
486
|
|
|
480
487
|
// Captain pane display — PBRT-3 / PBRT-14.
|
|
481
|
-
// The Captain pane is a stream
|
|
482
|
-
// parse each line at a
|
|
483
|
-
//
|
|
484
|
-
//
|
|
485
|
-
//
|
|
486
|
-
// ⤷
|
|
487
|
-
//
|
|
488
|
-
//
|
|
488
|
+
// The Captain pane is a stream of three glyphs plus one bare
|
|
489
|
+
// captain-speech act, designed so a reader can parse each line at a
|
|
490
|
+
// glance:
|
|
491
|
+
// (no glyph) bare FSM event type — host renders as captain speech
|
|
492
|
+
// (e.g., `captain> START_CODING`)
|
|
493
|
+
// ⤷ captain-invoking state entry: `<Player>: <label>`
|
|
494
|
+
// → transition guard outcome (`· field=N` tallies
|
|
495
|
+
// appended); the host presenter owns any visual
|
|
496
|
+
// nesting under the preceding ⤷ entry
|
|
497
|
+
|
|
498
|
+
// ◆ failure state (with `lastError` data) and the
|
|
499
|
+
// `awaitBossReply` suspension state (custom payload)
|
|
500
|
+
// The runtime emits no status line on entry to the idle state
|
|
501
|
+
// (`ready`) or the terminal state (`done`); the next `boss>` prompt
|
|
502
|
+
// is the implicit "turn over" signal. Prompts and full player output
|
|
503
|
+
// ride the player panes; the Captain pane keeps to the state-machine
|
|
504
|
+
// shape.
|
|
489
505
|
|
|
490
506
|
const STATE_LABELS: Readonly<Record<string, string>> = {
|
|
491
507
|
planAndImplement: 'plan & implement',
|
|
@@ -569,16 +585,21 @@ const QUIESCENT_STATES: ReadonlySet<string> = new Set([
|
|
|
569
585
|
'failed',
|
|
570
586
|
]);
|
|
571
587
|
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
588
|
+
// States whose entry the runtime does not surface on the Captain
|
|
589
|
+
// pane per PBRT-3: the readline returning to its `boss>` prompt is
|
|
590
|
+
// the implicit "turn over" signal, so a `◆ ready` / `◆ done`
|
|
591
|
+
// tombstone is redundant.
|
|
592
|
+
const SUPPRESSED_ENTRY_STATES: ReadonlySet<string> = new Set([
|
|
593
|
+
'ready',
|
|
594
|
+
'done',
|
|
595
|
+
]);
|
|
577
596
|
|
|
578
597
|
// Captain-pane surface (PBRT-3): every captain-invoking state plus
|
|
579
|
-
// the quiescent states
|
|
580
|
-
//
|
|
581
|
-
//
|
|
598
|
+
// the quiescent states whose entry still carries information
|
|
599
|
+
// (failure with `lastError`, awaitBossReply with the pending
|
|
600
|
+
// question). `ready` and `done` flow through the inspect handler
|
|
601
|
+
// but their entries are dropped before emitStatus per
|
|
602
|
+
// SUPPRESSED_ENTRY_STATES above.
|
|
582
603
|
const CAPTAIN_PANE_STATES: ReadonlySet<string> = new Set([
|
|
583
604
|
...stateMetadata.keys(),
|
|
584
605
|
...QUIESCENT_STATES,
|
|
@@ -633,12 +654,13 @@ function formatAwaitBossReplyEntry(context: Record<string, unknown>): string {
|
|
|
633
654
|
function formatStateEntry(
|
|
634
655
|
stateId: string,
|
|
635
656
|
context: Record<string, unknown> = {},
|
|
636
|
-
): string {
|
|
657
|
+
): string | undefined {
|
|
637
658
|
if (stateId === 'awaitBossReply') return formatAwaitBossReplyEntry(context);
|
|
638
|
-
if (
|
|
659
|
+
if (SUPPRESSED_ENTRY_STATES.has(stateId)) return undefined;
|
|
660
|
+
if (stateId === 'failed') return '◆ failed';
|
|
639
661
|
const meta = stateMetadata.get(stateId);
|
|
640
|
-
if (!meta) return
|
|
641
|
-
return
|
|
662
|
+
if (!meta) return `⤷ ${stateId}`;
|
|
663
|
+
return `⤷ ${meta.player}: ${meta.label}`;
|
|
642
664
|
}
|
|
643
665
|
|
|
644
666
|
function formatTransition(event: unknown): string | undefined {
|
|
@@ -652,32 +674,16 @@ function formatTransition(event: unknown): string | undefined {
|
|
|
652
674
|
tallies.push(`${field}=${items ? items.length : 1}`);
|
|
653
675
|
}
|
|
654
676
|
}
|
|
655
|
-
const suffix = tallies.length > 0 ? `
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
return
|
|
661
|
-
? `▸ BOSS ${text} → ${eventType}`
|
|
662
|
-
: `▸ BOSS ${text}`;
|
|
677
|
+
const suffix = tallies.length > 0 ? ` · ${tallies.join(' · ')}` : '';
|
|
678
|
+
// No leading whitespace: visual nesting under the preceding ⤷
|
|
679
|
+
// entry is the host presenter's concern (cligent's writeStatusLine
|
|
680
|
+
// emits status messages verbatim, with its own chrome but no
|
|
681
|
+
// continuation indent; layout is its job, not ours).
|
|
682
|
+
return `→ ${output.guard}${suffix}`;
|
|
663
683
|
}
|
|
664
684
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
// captain-invoking state is being entered. Routing-only context
|
|
668
|
-
// (reviewSubject, afterReview, etc.) stays out of the pane and is
|
|
669
|
-
// visible via emitTelemetry instead.
|
|
670
|
-
const RIDER_FIELDS = ['intent', 'irNumber', 'taskDescription'] as const;
|
|
671
|
-
|
|
672
|
-
function formatRiders(context: Record<string, unknown>): string {
|
|
673
|
-
const parts: string[] = [];
|
|
674
|
-
for (const f of RIDER_FIELDS) {
|
|
675
|
-
const v = context[f];
|
|
676
|
-
if (typeof v === 'string' && v.length > 0) {
|
|
677
|
-
parts.push(`${f}=${JSON.stringify(v)}`);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
return parts.length > 0 ? ` ${parts.join(' ')}` : '';
|
|
685
|
+
function formatClassification(eventType: string): string {
|
|
686
|
+
return eventType;
|
|
681
687
|
}
|
|
682
688
|
|
|
683
689
|
function stateTelemetryPayload(
|
|
@@ -712,8 +718,7 @@ export const _internal = {
|
|
|
712
718
|
formatAwaitBossReplyEntry,
|
|
713
719
|
formatStateEntry,
|
|
714
720
|
formatTransition,
|
|
715
|
-
|
|
716
|
-
formatRiders,
|
|
721
|
+
formatClassification,
|
|
717
722
|
stateTelemetryPayload,
|
|
718
723
|
};
|
|
719
724
|
|
|
@@ -805,16 +810,13 @@ export default function createPlaybookRuntime(
|
|
|
805
810
|
enqueueEmit(() => ports.emitStatus(transitionLine));
|
|
806
811
|
}
|
|
807
812
|
const entryLine = formatStateEntry(to, context);
|
|
808
|
-
|
|
809
|
-
? formatRiders(context)
|
|
810
|
-
: '';
|
|
811
|
-
const message = entryLine + riderSuffix;
|
|
813
|
+
if (entryLine === undefined) return;
|
|
812
814
|
if (to === 'failed') {
|
|
813
815
|
const lastError = (snap.context as { lastError?: unknown })
|
|
814
816
|
?.lastError;
|
|
815
|
-
enqueueEmit(() => ports.emitStatus(
|
|
817
|
+
enqueueEmit(() => ports.emitStatus(entryLine, { lastError }));
|
|
816
818
|
} else {
|
|
817
|
-
enqueueEmit(() => ports.emitStatus(
|
|
819
|
+
enqueueEmit(() => ports.emitStatus(entryLine));
|
|
818
820
|
}
|
|
819
821
|
},
|
|
820
822
|
},
|
|
@@ -856,13 +858,14 @@ export default function createPlaybookRuntime(
|
|
|
856
858
|
await drainEmissions();
|
|
857
859
|
return;
|
|
858
860
|
}
|
|
859
|
-
// 2. Captain-pane
|
|
860
|
-
//
|
|
861
|
-
//
|
|
862
|
-
//
|
|
861
|
+
// 2. Captain-pane classification line (PBRT-14): the bare
|
|
862
|
+
// FSM event type, emitted before the FSM advances so the
|
|
863
|
+
// host can render it as captain speech (e.g.,
|
|
864
|
+
// `captain> START_CODING`). Enqueued so it interleaves
|
|
865
|
+
// cleanly with the inspect-driven transition emissions.
|
|
863
866
|
const echoPorts = savedPorts;
|
|
864
867
|
enqueueEmit(() =>
|
|
865
|
-
echoPorts.emitStatus(
|
|
868
|
+
echoPorts.emitStatus(formatClassification(event.type)),
|
|
866
869
|
);
|
|
867
870
|
// 3. final state ('done') cannot accept new events — dispose
|
|
868
871
|
// and reconstruct per DR-004 §5.
|
|
@@ -2,31 +2,48 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
import createPlaybookRuntime from './code.playbook.js';
|
|
4
4
|
// Captain factory per TMUX-014: `(options: unknown) => Captain`.
|
|
5
|
-
// `options` is whatever `captain.options` carries in the YAML config
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// `options` is whatever `captain.options` carries in the YAML config.
|
|
6
|
+
// The per-run player identity strings (`coderPlayer`, `reviewerPlayer`)
|
|
7
|
+
// are derived from `session.players` at init time per PBRT-4 —
|
|
8
|
+
// preferring each entry's `model` and falling back to `adapter` when
|
|
9
|
+
// no model is pinned — so player prompts and commit-message trailers
|
|
10
|
+
// carry the concrete model identity (e.g. `claude-opus-4-7`) rather
|
|
11
|
+
// than the adapter family name (e.g. `claude`). Any same-named keys
|
|
12
|
+
// in `options` are ignored.
|
|
8
13
|
export default function createCodeTmuxPlayCaptain(options) {
|
|
9
|
-
const runtime = createPlaybookRuntime(options);
|
|
10
14
|
// CaptainSession is bound at init time and persists across turns;
|
|
11
15
|
// CaptainContext is rebuilt per turn and carries the call
|
|
12
|
-
// primitives.
|
|
13
|
-
//
|
|
14
|
-
//
|
|
16
|
+
// primitives. The runtime is constructed in `init` so identity
|
|
17
|
+
// strings derived from `session.players` can flow into its options;
|
|
18
|
+
// PlaybookPorts is built once at init, so the per-turn context
|
|
19
|
+
// lives in a closure-scoped slot the port callbacks query lazily.
|
|
20
|
+
let runtime;
|
|
15
21
|
let activeContext;
|
|
16
22
|
return {
|
|
17
23
|
async init(session) {
|
|
24
|
+
const playerIdentity = (id) => {
|
|
25
|
+
const entry = session.players.find((p) => p.id === id);
|
|
26
|
+
return entry?.model ?? entry?.adapter;
|
|
27
|
+
};
|
|
28
|
+
const coderPlayer = playerIdentity('coder');
|
|
29
|
+
const reviewerPlayer = playerIdentity('reviewer');
|
|
30
|
+
runtime = createPlaybookRuntime({
|
|
31
|
+
...options,
|
|
32
|
+
coderPlayer,
|
|
33
|
+
reviewerPlayer,
|
|
34
|
+
});
|
|
18
35
|
const ports = {
|
|
19
36
|
callPlayer: async (playerId, prompt, _signal) => {
|
|
20
37
|
if (!activeContext) {
|
|
21
38
|
throw new Error('callPlayer invoked outside a Boss turn');
|
|
22
39
|
}
|
|
23
|
-
//
|
|
40
|
+
// PlayerRunResult per TMUX-033 already matches PlayerResult
|
|
24
41
|
// (`status: 'ok' | 'aborted' | 'error'`, `finalText?`,
|
|
25
42
|
// `error?`). cligent honors context.signal internally;
|
|
26
43
|
// the runtime's signal is the same source forwarded
|
|
27
44
|
// through handleBossInput, so dropping `_signal` is
|
|
28
45
|
// safe.
|
|
29
|
-
const r = await activeContext.
|
|
46
|
+
const r = await activeContext.callPlayer(playerId, prompt);
|
|
30
47
|
return {
|
|
31
48
|
status: r.status,
|
|
32
49
|
finalText: r.finalText,
|
|
@@ -56,6 +73,9 @@ export default function createCodeTmuxPlayCaptain(options) {
|
|
|
56
73
|
await runtime.init(ports);
|
|
57
74
|
},
|
|
58
75
|
async handleBossTurn(turn, context) {
|
|
76
|
+
if (!runtime) {
|
|
77
|
+
throw new Error('init must be called first');
|
|
78
|
+
}
|
|
59
79
|
activeContext = context;
|
|
60
80
|
try {
|
|
61
81
|
// Forward the Boss prompt + cligent's per-turn signal into
|
|
@@ -71,7 +91,7 @@ export default function createCodeTmuxPlayCaptain(options) {
|
|
|
71
91
|
}
|
|
72
92
|
},
|
|
73
93
|
async dispose() {
|
|
74
|
-
await runtime
|
|
94
|
+
await runtime?.dispose();
|
|
75
95
|
},
|
|
76
96
|
};
|
|
77
97
|
}
|
|
@@ -18,37 +18,51 @@ import createPlaybookRuntime, {
|
|
|
18
18
|
} from './code.playbook.js';
|
|
19
19
|
|
|
20
20
|
// Captain factory per TMUX-014: `(options: unknown) => Captain`.
|
|
21
|
-
// `options` is whatever `captain.options` carries in the YAML config
|
|
22
|
-
//
|
|
23
|
-
//
|
|
21
|
+
// `options` is whatever `captain.options` carries in the YAML config.
|
|
22
|
+
// The per-run player identity strings (`coderPlayer`, `reviewerPlayer`)
|
|
23
|
+
// are derived from `session.players` at init time per PBRT-4 —
|
|
24
|
+
// preferring each entry's `model` and falling back to `adapter` when
|
|
25
|
+
// no model is pinned — so player prompts and commit-message trailers
|
|
26
|
+
// carry the concrete model identity (e.g. `claude-opus-4-7`) rather
|
|
27
|
+
// than the adapter family name (e.g. `claude`). Any same-named keys
|
|
28
|
+
// in `options` are ignored.
|
|
24
29
|
export default function createCodeTmuxPlayCaptain(
|
|
25
30
|
options: unknown,
|
|
26
31
|
): Captain {
|
|
27
|
-
const runtime: PlaybookRuntime = createPlaybookRuntime(
|
|
28
|
-
options as CodePlaybookOptions,
|
|
29
|
-
);
|
|
30
|
-
|
|
31
32
|
// CaptainSession is bound at init time and persists across turns;
|
|
32
33
|
// CaptainContext is rebuilt per turn and carries the call
|
|
33
|
-
// primitives.
|
|
34
|
-
//
|
|
35
|
-
//
|
|
34
|
+
// primitives. The runtime is constructed in `init` so identity
|
|
35
|
+
// strings derived from `session.players` can flow into its options;
|
|
36
|
+
// PlaybookPorts is built once at init, so the per-turn context
|
|
37
|
+
// lives in a closure-scoped slot the port callbacks query lazily.
|
|
38
|
+
let runtime: PlaybookRuntime | undefined;
|
|
36
39
|
let activeContext: CaptainContext | undefined;
|
|
37
40
|
|
|
38
41
|
return {
|
|
39
42
|
async init(session: CaptainSession): Promise<void> {
|
|
43
|
+
const playerIdentity = (id: string): string | undefined => {
|
|
44
|
+
const entry = session.players.find((p) => p.id === id);
|
|
45
|
+
return entry?.model ?? entry?.adapter;
|
|
46
|
+
};
|
|
47
|
+
const coderPlayer = playerIdentity('coder');
|
|
48
|
+
const reviewerPlayer = playerIdentity('reviewer');
|
|
49
|
+
runtime = createPlaybookRuntime({
|
|
50
|
+
...(options as CodePlaybookOptions),
|
|
51
|
+
coderPlayer,
|
|
52
|
+
reviewerPlayer,
|
|
53
|
+
});
|
|
40
54
|
const ports: PlaybookPorts = {
|
|
41
55
|
callPlayer: async (playerId, prompt, _signal) => {
|
|
42
56
|
if (!activeContext) {
|
|
43
57
|
throw new Error('callPlayer invoked outside a Boss turn');
|
|
44
58
|
}
|
|
45
|
-
//
|
|
59
|
+
// PlayerRunResult per TMUX-033 already matches PlayerResult
|
|
46
60
|
// (`status: 'ok' | 'aborted' | 'error'`, `finalText?`,
|
|
47
61
|
// `error?`). cligent honors context.signal internally;
|
|
48
62
|
// the runtime's signal is the same source forwarded
|
|
49
63
|
// through handleBossInput, so dropping `_signal` is
|
|
50
64
|
// safe.
|
|
51
|
-
const r = await activeContext.
|
|
65
|
+
const r = await activeContext.callPlayer(playerId, prompt);
|
|
52
66
|
return {
|
|
53
67
|
status: r.status,
|
|
54
68
|
finalText: r.finalText,
|
|
@@ -89,6 +103,9 @@ export default function createCodeTmuxPlayCaptain(
|
|
|
89
103
|
turn: BossTurn,
|
|
90
104
|
context: CaptainContext,
|
|
91
105
|
): Promise<void> {
|
|
106
|
+
if (!runtime) {
|
|
107
|
+
throw new Error('init must be called first');
|
|
108
|
+
}
|
|
92
109
|
activeContext = context;
|
|
93
110
|
try {
|
|
94
111
|
// Forward the Boss prompt + cligent's per-turn signal into
|
|
@@ -104,7 +121,7 @@ export default function createCodeTmuxPlayCaptain(
|
|
|
104
121
|
},
|
|
105
122
|
|
|
106
123
|
async dispose(): Promise<void> {
|
|
107
|
-
await runtime
|
|
124
|
+
await runtime?.dispose();
|
|
108
125
|
},
|
|
109
126
|
};
|
|
110
127
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
|
|
4
|
+
# Seed template for the user-level playbook-code config.
|
|
5
|
+
# First run copies this file to:
|
|
6
|
+
# ${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook-code.config.yaml
|
|
7
|
+
#
|
|
8
|
+
# Safe tuning points:
|
|
9
|
+
# - Change captain.adapter / captain.model / captain.reasoningEffort to
|
|
10
|
+
# pick the judge/Captain agent and its reasoning tier.
|
|
11
|
+
# - Change each player's adapter, model, and reasoningEffort to pick
|
|
12
|
+
# the Coder and Reviewer agents.
|
|
13
|
+
#
|
|
14
|
+
# PBRT-4 host-configuration invariants:
|
|
15
|
+
# - captain.from must keep pointing at the CODE tmux-play adapter module.
|
|
16
|
+
# - players[].id must remain exactly "coder" and "reviewer"; the runtime routes
|
|
17
|
+
# to those baked ids and does not remap them. The adapter derives the
|
|
18
|
+
# per-run player identity strings (substituted into <coder-llm> /
|
|
19
|
+
# <reviewer-llm> in player prompts) from each entry's model when pinned
|
|
20
|
+
# and falls back to its adapter when no model is set, so the Committer's
|
|
21
|
+
# commit-message trailers can name the concrete model.
|
|
22
|
+
|
|
23
|
+
captain:
|
|
24
|
+
# PBRT-4 invariant: keep this adapter module path unchanged.
|
|
25
|
+
from: "@sublang/playbook/code/tmux-play"
|
|
26
|
+
# Tunable: the Captain/Judge adapter, model, and reasoning effort.
|
|
27
|
+
adapter: claude
|
|
28
|
+
model: claude-sonnet-4-6
|
|
29
|
+
reasoningEffort: high
|
|
30
|
+
permissions:
|
|
31
|
+
mode: auto
|
|
32
|
+
|
|
33
|
+
players:
|
|
34
|
+
# PBRT-4 invariant: keep id: coder. Tune adapter/model if desired.
|
|
35
|
+
- id: coder
|
|
36
|
+
adapter: claude
|
|
37
|
+
model: claude-opus-4-7
|
|
38
|
+
reasoningEffort: xhigh
|
|
39
|
+
permissions:
|
|
40
|
+
mode: auto
|
|
41
|
+
# PBRT-4 invariant: keep id: reviewer. Tune adapter/model if desired.
|
|
42
|
+
- id: reviewer
|
|
43
|
+
adapter: codex
|
|
44
|
+
model: gpt-5.5
|
|
45
|
+
reasoningEffort: xhigh
|
|
46
|
+
permissions:
|
|
47
|
+
mode: auto
|
|
@@ -10,32 +10,36 @@ captain:
|
|
|
10
10
|
# @sublang/playbook is published, swap this for the package
|
|
11
11
|
# specifier — e.g.
|
|
12
12
|
# from: "@sublang/playbook/code/tmux-play"
|
|
13
|
-
# (final form confirmed at publish time per DR-004 §11).
|
|
13
|
+
# (final form confirmed at publish time per DR-004 §11). Players,
|
|
14
14
|
# options, and the rest of the config are unchanged.
|
|
15
15
|
from: ./code.tmux-play.js
|
|
16
16
|
adapter: claude
|
|
17
|
-
model: claude-
|
|
17
|
+
model: claude-sonnet-4-6
|
|
18
|
+
reasoningEffort: high
|
|
18
19
|
# Agents run in cligent's classifier/reviewer-protected auto mode
|
|
19
20
|
# (cligent DR-005): claude → permissionMode auto, codex → auto_review.
|
|
20
21
|
permissions:
|
|
21
22
|
mode: auto
|
|
22
|
-
options:
|
|
23
|
-
# Substituted into the <coder-llm> placeholder in player prompts
|
|
24
|
-
# (DR-004 §6).
|
|
25
|
-
coderPlayer: claude
|
|
26
|
-
# Substituted into <reviewer-llm>.
|
|
27
|
-
reviewerPlayer: codex
|
|
28
23
|
|
|
29
|
-
# `
|
|
24
|
+
# `players[].id` shall match the baked playerId strings the adapter
|
|
30
25
|
# routes to. The CODE playbook bakes Coder→'coder' and
|
|
31
26
|
# Reviewer→'reviewer' at link time (DR-004 §2); the adapter does
|
|
32
|
-
# not remap, so changing these ids would break
|
|
33
|
-
|
|
27
|
+
# not remap, so changing these ids would break callPlayer routing.
|
|
28
|
+
# Each entry's `model` (when pinned) or `adapter` (otherwise) is
|
|
29
|
+
# substituted into the <coder-llm> / <reviewer-llm> placeholders in
|
|
30
|
+
# player prompts (DR-004 §6, PBRT-4) — pin a concrete `model:` if
|
|
31
|
+
# you want the Committer's commit-message trailers to name the
|
|
32
|
+
# specific model rather than the adapter family.
|
|
33
|
+
players:
|
|
34
34
|
- id: coder
|
|
35
35
|
adapter: claude
|
|
36
|
+
model: claude-opus-4-7
|
|
37
|
+
reasoningEffort: xhigh
|
|
36
38
|
permissions:
|
|
37
39
|
mode: auto
|
|
38
40
|
- id: reviewer
|
|
39
41
|
adapter: codex
|
|
42
|
+
model: gpt-5.5
|
|
43
|
+
reasoningEffort: xhigh
|
|
40
44
|
permissions:
|
|
41
45
|
mode: auto
|
|
@@ -9,19 +9,21 @@
|
|
|
9
9
|
captain:
|
|
10
10
|
from: "@sublang/playbook/code/tmux-play"
|
|
11
11
|
adapter: claude
|
|
12
|
-
model: claude-
|
|
12
|
+
model: claude-sonnet-4-6
|
|
13
|
+
reasoningEffort: high
|
|
13
14
|
permissions:
|
|
14
15
|
mode: auto
|
|
15
|
-
options:
|
|
16
|
-
coderPlayer: claude
|
|
17
|
-
reviewerPlayer: codex
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
players:
|
|
20
18
|
- id: coder
|
|
21
19
|
adapter: claude
|
|
20
|
+
model: claude-opus-4-7
|
|
21
|
+
reasoningEffort: xhigh
|
|
22
22
|
permissions:
|
|
23
23
|
mode: auto
|
|
24
24
|
- id: reviewer
|
|
25
25
|
adapter: codex
|
|
26
|
+
model: gpt-5.5
|
|
27
|
+
reasoningEffort: xhigh
|
|
26
28
|
permissions:
|
|
27
29
|
mode: auto
|