@sublang/playbook 0.4.2 → 0.5.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 +27 -15
- package/package.json +6 -4
- package/reference/sdlc/code.playbook/bin/playbook-code.js +248 -105
- package/reference/sdlc/code.playbook/code.fsm.d.ts +1 -0
- package/reference/sdlc/code.playbook/code.fsm.js +51 -21
- package/reference/sdlc/code.playbook/code.fsm.ts +58 -21
- package/reference/sdlc/code.playbook/code.gears.md +50 -22
- package/reference/sdlc/code.playbook/code.playbook.d.ts +19 -3
- package/reference/sdlc/code.playbook/code.playbook.js +292 -34
- package/reference/sdlc/code.playbook/code.playbook.ts +290 -35
- package/reference/sdlc/code.playbook/code.tmux-play.d.ts +4 -0
- package/reference/sdlc/code.playbook/code.tmux-play.js +75 -12
- package/reference/sdlc/code.playbook/code.tmux-play.ts +88 -12
- package/reference/sdlc/code.playbook/playbook-code.config.template.yaml +41 -25
|
@@ -17,15 +17,75 @@ import createPlaybookRuntime, {
|
|
|
17
17
|
type PlaybookRuntime,
|
|
18
18
|
} from './code.playbook.js';
|
|
19
19
|
|
|
20
|
+
// PBRT-29/30: CODE runtime options are carried under
|
|
21
|
+
// `captain.options.code`, a namespaced object the host forwards
|
|
22
|
+
// verbatim through `captain.options`. cligent neither reads nor
|
|
23
|
+
// validates `options.code` (PBRT-30); this adapter is the sole
|
|
24
|
+
// validator. The CODE options schema defines one key, `committer`: an
|
|
25
|
+
// optional Committer-alias player id, one of the baked player ids
|
|
26
|
+
// `coder` / `reviewer`. A valid `options.code` is absent, `{}`, or
|
|
27
|
+
// `{ committer: 'coder' | 'reviewer' }`; every other key is unknown
|
|
28
|
+
// and rejected with a path-named error, and an out-of-range
|
|
29
|
+
// `committer` value is rejected naming `captain.options.code.committer`.
|
|
30
|
+
// A further CODE option shall be introduced as its own higher-numbered
|
|
31
|
+
// item that widens `CODE_OPTION_KEYS`; the validator still fails closed
|
|
32
|
+
// on stray keys.
|
|
33
|
+
const CODE_OPTION_KEYS = new Set<string>(['committer']);
|
|
34
|
+
const COMMITTER_PLAYER_IDS = new Set<string>(['coder', 'reviewer']);
|
|
35
|
+
|
|
36
|
+
// The validated CODE options set. `committer`, when present, is the
|
|
37
|
+
// resolved Committer-alias player id (PBRT-8 / PBRT-30); a future CODE
|
|
38
|
+
// option widens both this type and `CODE_OPTION_KEYS`.
|
|
39
|
+
export interface CodeOptions {
|
|
40
|
+
committer?: 'coder' | 'reviewer';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function validateCodeOptions(captainOptions: unknown): CodeOptions {
|
|
44
|
+
const code = readCodeNamespace(captainOptions);
|
|
45
|
+
if (code === undefined) return {};
|
|
46
|
+
if (typeof code !== 'object' || code === null || Array.isArray(code)) {
|
|
47
|
+
throw new Error('captain.options.code must be an object');
|
|
48
|
+
}
|
|
49
|
+
for (const key of Object.keys(code)) {
|
|
50
|
+
if (!CODE_OPTION_KEYS.has(key)) {
|
|
51
|
+
throw new Error(`Unknown config field captain.options.code.${key}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const options: CodeOptions = {};
|
|
55
|
+
const committer = (code as Record<string, unknown>).committer;
|
|
56
|
+
if (committer !== undefined) {
|
|
57
|
+
if (typeof committer !== 'string' || !COMMITTER_PLAYER_IDS.has(committer)) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"captain.options.code.committer must be 'coder' or 'reviewer'",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
options.committer = committer as 'coder' | 'reviewer';
|
|
63
|
+
}
|
|
64
|
+
return options;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readCodeNamespace(captainOptions: unknown): unknown {
|
|
68
|
+
if (
|
|
69
|
+
typeof captainOptions !== 'object' ||
|
|
70
|
+
captainOptions === null ||
|
|
71
|
+
Array.isArray(captainOptions)
|
|
72
|
+
) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
return (captainOptions as Record<string, unknown>).code;
|
|
76
|
+
}
|
|
77
|
+
|
|
20
78
|
// Captain factory per TMUX-014: `(options: unknown) => Captain`.
|
|
21
|
-
// `options` is whatever `captain.options` carries in the YAML config
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
79
|
+
// `options` is whatever `captain.options` carries in the YAML config;
|
|
80
|
+
// CODE reads only the namespaced `options.code` (PBRT-30). The per-run
|
|
81
|
+
// player identity strings (`coderPlayer`, `reviewerPlayer`) are
|
|
82
|
+
// derived from `session.players` at init time per PBRT-4 — preferring
|
|
83
|
+
// each entry's `model` and falling back to `adapter` when no model is
|
|
84
|
+
// pinned — so player prompts and commit-message trailers carry the
|
|
85
|
+
// concrete model identity (e.g. `claude-opus-4-7`) rather than the
|
|
86
|
+
// adapter family name (e.g. `claude`). They come from `session.players`
|
|
87
|
+
// independent of `captain.options.code` and override any same-named
|
|
88
|
+
// keys.
|
|
29
89
|
export default function createCodeTmuxPlayCaptain(
|
|
30
90
|
options: unknown,
|
|
31
91
|
): Captain {
|
|
@@ -40,17 +100,28 @@ export default function createCodeTmuxPlayCaptain(
|
|
|
40
100
|
|
|
41
101
|
return {
|
|
42
102
|
async init(session: CaptainSession): Promise<void> {
|
|
103
|
+
// PBRT-30: validate `captain.options.code` before constructing
|
|
104
|
+
// the runtime so a stray key fails `init` closed with a
|
|
105
|
+
// path-named error; the empty schema yields an empty options set.
|
|
106
|
+
const codeOptions = validateCodeOptions(options);
|
|
43
107
|
const playerIdentity = (id: string): string | undefined => {
|
|
44
108
|
const entry = session.players.find((p) => p.id === id);
|
|
45
109
|
return entry?.model ?? entry?.adapter;
|
|
46
110
|
};
|
|
47
111
|
const coderPlayer = playerIdentity('coder');
|
|
48
112
|
const reviewerPlayer = playerIdentity('reviewer');
|
|
49
|
-
|
|
50
|
-
|
|
113
|
+
// Identity strings from `session.players` override any same-named
|
|
114
|
+
// keys and are independent of `captain.options.code` (PBRT-30).
|
|
115
|
+
// The validated `committer` alias threads in as the runtime's
|
|
116
|
+
// Committer player id (`committerPlayer`, PBRT-8).
|
|
117
|
+
const runtimeOptions: CodePlaybookOptions = {
|
|
51
118
|
coderPlayer,
|
|
52
119
|
reviewerPlayer,
|
|
53
|
-
|
|
120
|
+
...(codeOptions.committer !== undefined
|
|
121
|
+
? { committerPlayer: codeOptions.committer }
|
|
122
|
+
: {}),
|
|
123
|
+
};
|
|
124
|
+
runtime = createPlaybookRuntime(runtimeOptions);
|
|
54
125
|
const ports: PlaybookPorts = {
|
|
55
126
|
callPlayer: async (playerId, prompt, _signal) => {
|
|
56
127
|
if (!activeContext) {
|
|
@@ -73,7 +144,12 @@ export default function createCodeTmuxPlayCaptain(
|
|
|
73
144
|
if (!activeContext) {
|
|
74
145
|
throw new Error('callJudge invoked outside a Boss turn');
|
|
75
146
|
}
|
|
76
|
-
|
|
147
|
+
// PBRT-15 / DR-007: run the judge call hidden so its JSON
|
|
148
|
+
// reply never reaches the Boss pane; the runtime composes the
|
|
149
|
+
// human-readable pane lines (PBRT-3) from the parsed result.
|
|
150
|
+
const r = await activeContext.callCaptain(prompt, {
|
|
151
|
+
visibility: 'hidden',
|
|
152
|
+
});
|
|
77
153
|
if (r.status !== 'ok') {
|
|
78
154
|
throw new Error(
|
|
79
155
|
r.error ?? `callCaptain status "${r.status}"`,
|
|
@@ -1,29 +1,44 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
# SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
|
|
4
|
-
# Seed template for the user-level playbook-code
|
|
4
|
+
# Seed template for the user-level playbook-code CODE overlay.
|
|
5
5
|
# First run copies this file to:
|
|
6
6
|
# ${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook-code.config.yaml
|
|
7
7
|
#
|
|
8
|
-
#
|
|
9
|
-
# -
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
8
|
+
# This is a CODE *overlay*, not a full tmux-play config: playbook-code
|
|
9
|
+
# composes the launched tmux-play config from it at startup (PBCODE-16).
|
|
10
|
+
# The composer injects captain.from (the CODE adapter module) and the
|
|
11
|
+
# coder / reviewer player ids, and can inherit theme plus captain-judge
|
|
12
|
+
# defaults from an existing tmux-play config — so you only tune the
|
|
13
|
+
# fields below.
|
|
13
14
|
#
|
|
14
|
-
#
|
|
15
|
-
# - captain.
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
15
|
+
# Safe tuning points:
|
|
16
|
+
# - captain.adapter / model / reasoningEffort pick the judge/Captain
|
|
17
|
+
# agent and its reasoning tier.
|
|
18
|
+
# - players.coder and players.reviewer pick the Coder and Reviewer
|
|
19
|
+
# agents; set each role's adapter (required) and optional model /
|
|
20
|
+
# reasoningEffort / permissions. Each role's model when pinned, else
|
|
21
|
+
# its adapter, doubles as the identity string the adapter substitutes
|
|
22
|
+
# into <coder-llm> / <reviewer-llm> in player prompts (PBRT-4), so pin
|
|
23
|
+
# a model if you want the Committer's commit-message trailers to name
|
|
24
|
+
# the concrete model.
|
|
25
|
+
# - Codex roles that run git under mode: auto need .git in
|
|
26
|
+
# permissions.writablePaths so git metadata writes stay profile-scoped
|
|
27
|
+
# instead of requiring bypass permissions.
|
|
28
|
+
# - players.committer optionally aliases the Committer to a role
|
|
29
|
+
# (coder or reviewer); that role's pane runs the commit turn. The
|
|
30
|
+
# seeded value is reviewer; omit it to fall back to the coder.
|
|
31
|
+
# - layout sizes the tmux window (layout.window: columns × rows) and
|
|
32
|
+
# sets the relative column widths (layout.columnWeights) for the
|
|
33
|
+
# Boss/Captain, Coder, and Reviewer columns.
|
|
34
|
+
|
|
35
|
+
layout:
|
|
36
|
+
window:
|
|
37
|
+
columns: 174
|
|
38
|
+
rows: 49
|
|
39
|
+
columnWeights: [4, 6, 6]
|
|
22
40
|
|
|
23
41
|
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
42
|
adapter: claude
|
|
28
43
|
model: claude-sonnet-4-6
|
|
29
44
|
reasoningEffort: high
|
|
@@ -31,17 +46,18 @@ captain:
|
|
|
31
46
|
mode: auto
|
|
32
47
|
|
|
33
48
|
players:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
model: claude-opus-4-7
|
|
49
|
+
coder:
|
|
50
|
+
adapter: codex
|
|
51
|
+
model: gpt-5.5
|
|
38
52
|
reasoningEffort: xhigh
|
|
39
53
|
permissions:
|
|
40
54
|
mode: auto
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
writablePaths:
|
|
56
|
+
- .git
|
|
57
|
+
reviewer:
|
|
58
|
+
adapter: claude
|
|
59
|
+
model: claude-opus-4-8
|
|
45
60
|
reasoningEffort: xhigh
|
|
46
61
|
permissions:
|
|
47
62
|
mode: auto
|
|
63
|
+
committer: reviewer
|