@sublang/playbook 0.9.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -151
- package/package.json +46 -6
- package/reference/sdlc/captain.md +102 -0
- package/reference/sdlc/captain.playbook/captain.fsm.d.ts +227 -0
- package/reference/sdlc/captain.playbook/captain.fsm.js +628 -0
- package/reference/sdlc/captain.playbook/captain.fsm.ts +851 -0
- package/reference/sdlc/captain.playbook/captain.gears.md +60 -0
- package/reference/sdlc/captain.playbook/captain.playbook.d.ts +23 -0
- package/reference/sdlc/captain.playbook/captain.playbook.js +1053 -0
- package/reference/sdlc/captain.playbook/captain.playbook.ts +1144 -0
- package/reference/sdlc/code.playbook/bin/playbook.js +152 -10
- package/reference/sdlc/code.playbook/bin/run.js +893 -0
- package/reference/sdlc/code.playbook/code.fsm.d.ts +11 -4
- package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +2 -2
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +1 -1
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +6 -6
- package/reference/sdlc/code.playbook/code.fsm.js +334 -102
- package/reference/sdlc/code.playbook/code.fsm.ts +467 -180
- package/reference/sdlc/code.playbook/code.gears.md +11 -10
- package/reference/sdlc/code.playbook/code.playbook.d.ts +18 -9
- package/reference/sdlc/code.playbook/code.playbook.js +1095 -200
- package/reference/sdlc/code.playbook/code.playbook.ts +1437 -256
- package/reference/sdlc/code.playbook/code.registry.d.ts +0 -3
- package/reference/sdlc/code.playbook/code.registry.js +0 -3
- package/reference/sdlc/code.playbook/code.registry.ts +0 -6
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +9 -4
- package/reference/sdlc/code.playbook/playbook-captain.js +889 -210
- package/reference/sdlc/code.playbook/playbook-captain.ts +1136 -257
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +10 -0
- package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +396 -0
- package/reference/sdlc/discuss.playbook/discuss.fsm.js +2066 -0
- package/reference/sdlc/discuss.playbook/discuss.fsm.ts +2464 -0
- package/reference/sdlc/discuss.playbook/discuss.gears.md +251 -0
- package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +113 -0
- package/reference/sdlc/discuss.playbook/discuss.playbook.js +1514 -0
- package/reference/sdlc/discuss.playbook/discuss.playbook.ts +1926 -0
- package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +58 -0
- package/reference/sdlc/discuss.playbook/discuss.registry.js +97 -0
- package/reference/sdlc/discuss.playbook/discuss.registry.ts +153 -0
- package/slc/gears2fsm.md +557 -57
- package/slc/link.md +1097 -80
- package/slc/optimize.md +88 -0
- package/slc/text2gears.md +247 -5
- package/src/runtime.d.ts +145 -3
- package/src/runtime.ts +200 -2
- package/src/xstate-runtime.d.ts +94 -0
- package/src/xstate-runtime.js +1247 -0
- package/src/xstate-runtime.ts +1802 -0
package/README.md
CHANGED
|
@@ -7,28 +7,49 @@
|
|
|
7
7
|
[](https://nodejs.org/)
|
|
8
8
|
[](https://github.com/sublang-ai/playbook/actions/workflows/ci.yml)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
_Skills made reliable through state machines and visualization._
|
|
11
11
|
|
|
12
12
|
playbook is a compiler stack and reference implementation for turning a
|
|
13
13
|
natural-language procedure into a runnable, inspectable state-machine
|
|
14
|
-
agent — a
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
agent — a _playbook_ — that orchestrates other AI agents per a spec
|
|
15
|
+
written in plain prose. Instead of a free-form LLM deciding what to do
|
|
16
|
+
next, an explicit finite state machine drives the workflow, every
|
|
17
|
+
Captain-, player-, and nested-playbook-invoking state pinned 1:1 to a
|
|
18
|
+
human-readable spec item and contract-tested.
|
|
19
|
+
|
|
20
|
+
A quick vocabulary, used throughout: the **Boss** is you, the human in
|
|
21
|
+
charge; the **Captain** is the agent pane the Boss talks to;
|
|
22
|
+
**players** are the coding agents a playbook delegates work to; and a
|
|
23
|
+
hidden **judge** classifies free-text Boss input into state-machine
|
|
24
|
+
events, so playbooks need no slash commands of their own. Playbooks run
|
|
25
|
+
inside a *host* built on
|
|
26
|
+
[cligent](https://github.com/sublang-ai/cligent), the sibling SDK that
|
|
27
|
+
drives coding-agent CLIs; cligent's `tmux-play` terminal app is the
|
|
28
|
+
reference host.
|
|
29
|
+
|
|
30
|
+
Three phases take prose to runtime:
|
|
31
|
+
|
|
32
|
+
1. **text → GEARS** ([slc/text2gears.md](slc/text2gears.md)) — GEARS is
|
|
33
|
+
the intermediate representation: normative spec items, one per state
|
|
34
|
+
behavior, partitioned by trigger and prompt content.
|
|
20
35
|
2. **GEARS → FSM** ([slc/gears2fsm.md](slc/gears2fsm.md)) — an XState v5
|
|
21
|
-
finite state machine; each gear maps to one
|
|
22
|
-
with a typed
|
|
36
|
+
finite state machine; each gear maps to one direct-Captain,
|
|
37
|
+
delegated-player, or nested-playbook state with a typed actor
|
|
38
|
+
contract. The compiled FSM can be visualized and simulated with the
|
|
39
|
+
bundled [XState sketch visualizer](views/sketch).
|
|
23
40
|
3. **FSM → runtime** ([slc/link.md](slc/link.md)) — a host-agnostic
|
|
24
41
|
module that drives Boss turns through ports the host wires up
|
|
25
42
|
(cligent's `tmux-play` is one such host).
|
|
26
43
|
|
|
27
|
-
The repository
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
44
|
+
The repository contains end-to-end worked examples. The generic default
|
|
45
|
+
Captain is generated from
|
|
46
|
+
[`reference/sdlc/captain.md`](reference/sdlc/captain.md), CODE — a
|
|
47
|
+
coder / reviewer / committer development loop — from
|
|
48
|
+
[`reference/sdlc/code.md`](reference/sdlc/code.md), and DISCUSS — two
|
|
49
|
+
agents converging on spec items — from
|
|
50
|
+
[`reference/sdlc/discuss.md`](reference/sdlc/discuss.md). Together they
|
|
51
|
+
show direct Captain work, sequential nested playbook calls, and
|
|
52
|
+
parallel players.
|
|
32
53
|
|
|
33
54
|
## Getting started — the reference CODE playbook
|
|
34
55
|
|
|
@@ -40,7 +61,26 @@ the runtime registered behind the built-in Playbook Captain shell for
|
|
|
40
61
|
cligent's `tmux-play` host out of the box.
|
|
41
62
|
The compiled artifacts live under
|
|
42
63
|
[`reference/sdlc/code.playbook/`](reference/sdlc/code.playbook),
|
|
43
|
-
the slc pipeline's
|
|
64
|
+
the [slc](https://github.com/sublang-ai/slc) compiler pipeline's
|
|
65
|
+
`<basename>.<pipeline>/` output directory.
|
|
66
|
+
|
|
67
|
+
> **Release status:** 1.0.0 is the first release of the composed system: the
|
|
68
|
+
> compiled default Captain, CODE and DISCUSS, nested playbook calls, the
|
|
69
|
+
> semver-stable six-port runtime contract (see
|
|
70
|
+
> [docs/embedding.md](docs/embedding.md)), and non-interactive `playbook run`
|
|
71
|
+
> with parked-session resume and per-run agent tuning.
|
|
72
|
+
|
|
73
|
+
### Requirements
|
|
74
|
+
|
|
75
|
+
- Node.js >= 20.6.0.
|
|
76
|
+
- `tmux` and [`glow`](https://github.com/charmbracelet/glow#installation)
|
|
77
|
+
on `PATH` — the `tmux-play` host renders Markdown pane output with
|
|
78
|
+
glow and fails fast without it.
|
|
79
|
+
- Auth for the seeded agents: a signed-in
|
|
80
|
+
[Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview)
|
|
81
|
+
(`~/.claude`) or `ANTHROPIC_API_KEY`, and a signed-in
|
|
82
|
+
[Codex CLI](https://github.com/openai/codex) (`~/.codex`) or
|
|
83
|
+
`OPENAI_API_KEY`.
|
|
44
84
|
|
|
45
85
|
### Install (users)
|
|
46
86
|
|
|
@@ -91,6 +131,51 @@ bypass permissions. The seeded Codex Reviewer additionally grants
|
|
|
91
131
|
available under the Codex sandbox; the Claude agents need no such grant
|
|
92
132
|
under their auto mode.
|
|
93
133
|
|
|
134
|
+
### Run a playbook non-interactively
|
|
135
|
+
|
|
136
|
+
`playbook run <from> [task]` runs one playbook once, without tmux-play
|
|
137
|
+
and without a config entry — point it straight at a registry module:
|
|
138
|
+
|
|
139
|
+
```sh
|
|
140
|
+
playbook run @sublang/playbook/code/registry "add a test for parseArgs" \
|
|
141
|
+
--player coder=claude --player reviewer=codex --cwd ./my-repo
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`[task]` is read from stdin when omitted — pipe long or multi-line
|
|
145
|
+
intents the same way you would to `claude -p` or `codex exec`. Roles
|
|
146
|
+
and the captain default to `claude`; bind them with
|
|
147
|
+
`--player <role>=<agent>` and `--captain <agent>`, where `<agent>` is
|
|
148
|
+
`<adapter>[:<model>][@<effort>]` — say `codex:gpt-5.5@xhigh`, or
|
|
149
|
+
`claude@high` for the default model at high reasoning effort; the
|
|
150
|
+
model keeps every interior colon (`opencode:ollama/llama3:8b@max`),
|
|
151
|
+
and unsupported efforts are rejected up front naming the adapter's
|
|
152
|
+
supported values. Pass a playbook option with
|
|
153
|
+
`--option <key>=<value>`, and add `--json` to print one JSON envelope
|
|
154
|
+
(`outcome`, `sessionId`, and the output or pending questions) instead
|
|
155
|
+
of plain text. It exits `0` on a terminal outcome, `2` on failure, `3`
|
|
156
|
+
when the playbook needs a Boss reply, and `1` on a bad argument or
|
|
157
|
+
module. See [PBCLI-18](specs/user/playbook-cli.md#pbcli-18).
|
|
158
|
+
|
|
159
|
+
When the playbook stops to ask the Boss something, the run is parked,
|
|
160
|
+
not lost: the question prints to stdout, the session is saved under
|
|
161
|
+
`${XDG_STATE_HOME:-$HOME/.local/state}/playbook/sessions/`, and stderr
|
|
162
|
+
names the exact command that continues it. Answer with `resume`:
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
playbook run resume 4f2c…9ab1 "keep the scope small; skip the docs"
|
|
166
|
+
playbook run resume --last # most recently parked session; reply on stdin
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
A resumed run picks the playbook up exactly where it parked — same
|
|
170
|
+
session id, same workflow state, and each agent continues its own
|
|
171
|
+
conversation — then prints the final output and exits `0`, or parks
|
|
172
|
+
again with the next question and exits `3`. The agent lineup, options,
|
|
173
|
+
and working directory are stored with the session, so `resume` takes no
|
|
174
|
+
binding flags. In scripts, capture the session id from the `--json`
|
|
175
|
+
envelope, like Claude Code's `session_id` or `codex exec resume`. See
|
|
176
|
+
[PBCLI-22](specs/user/playbook-cli.md#pbcli-22) and
|
|
177
|
+
[DR-014](specs/decisions/014-durable-one-shot-run-sessions.md).
|
|
178
|
+
|
|
94
179
|
### Configure agents
|
|
95
180
|
|
|
96
181
|
Edit the seeded user config when you want different coding agents:
|
|
@@ -99,33 +184,44 @@ Edit the seeded user config when you want different coding agents:
|
|
|
99
184
|
$EDITOR "${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook.config.yaml"
|
|
100
185
|
```
|
|
101
186
|
|
|
187
|
+
To retune a single launch without editing that file, overlay a
|
|
188
|
+
fragment in the same format with `--with` (repeatable, later files
|
|
189
|
+
win; maps merge recursively, other values replace):
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
playbook --with fast-lineup.yaml
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```yaml
|
|
196
|
+
# fast-lineup.yaml — swap the Coder for one run; nothing is written back.
|
|
197
|
+
profiles:
|
|
198
|
+
codex-fast: { adapter: codex, model: gpt-5.5, reasoningEffort: medium }
|
|
199
|
+
playbooks:
|
|
200
|
+
code:
|
|
201
|
+
players:
|
|
202
|
+
coder: codex-fast
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
See [PBCLI-25](specs/user/playbook-cli.md#pbcli-25) and
|
|
206
|
+
[DR-015](specs/decisions/015-per-run-agent-tuning.md).
|
|
207
|
+
|
|
102
208
|
The config is top-level (no `config:` wrapper): a `profiles` map of
|
|
103
|
-
reusable agent settings, a `captain`
|
|
104
|
-
|
|
105
|
-
`
|
|
209
|
+
reusable agent settings, a `captain` agent (it runs both visible
|
|
210
|
+
Captain work and hidden judge calls), optional `layout` /
|
|
211
|
+
`notifications` / `theme`, and a `playbooks` map of enabled playbooks.
|
|
212
|
+
Each `captain` or `players.<role>` value is a profile id or an adapter
|
|
106
213
|
shorthand (`claude`, `codex`); other adapter ids are passed through to
|
|
107
214
|
`tmux-play` with a warning because `playbook` cannot preflight their
|
|
108
215
|
auth. Name profiles by their underlying agent/model (e.g. `claude-opus`,
|
|
109
|
-
`codex-gpt`) so the profile ids read distinctly from the
|
|
110
|
-
|
|
111
|
-
`command` (an optional slash-command override), and
|
|
112
|
-
launcher-owned; every other key
|
|
113
|
-
|
|
114
|
-
namespaced `<id>-<role>` host players, so you do not write those by
|
|
216
|
+
`codex-gpt`) so the profile ids read distinctly from the player roles
|
|
217
|
+
that reference them. Within a `playbooks.<id>` block, `from` (the
|
|
218
|
+
registry module), `command` (an optional slash-command override), and
|
|
219
|
+
`players` are launcher-owned; every other key is that playbook's option
|
|
220
|
+
slice. The launcher injects the rest — you do not write host wiring by
|
|
115
221
|
hand.
|
|
116
222
|
|
|
117
|
-
CODE's per-run `<coder-llm>` / `<reviewer-llm>` prompt strings come from
|
|
118
|
-
each role's pinned `model`, else its `adapter`
|
|
119
|
-
([PBRT-4](specs/user/playbook-runtime.md#pbrt-4)) — so the Committer's
|
|
120
|
-
commit-message trailers can name the concrete model
|
|
121
|
-
(e.g. `claude-opus-4-8[1m]`) rather than the adapter family (`claude`).
|
|
122
|
-
`committer` is an optional CODE alias naming which role — `coder` or
|
|
123
|
-
`reviewer` — runs the commit turn; the seeded config points it at the
|
|
124
|
-
Coder, and absent the alias the Committer falls back to the Coder
|
|
125
|
-
([PBRT-8](specs/user/playbook-runtime.md#pbrt-8)).
|
|
126
|
-
|
|
127
223
|
For example, the seeded config runs the Coder on Claude Opus 4.8 1m and
|
|
128
|
-
the Reviewer on GPT-5.5
|
|
224
|
+
the Reviewer on GPT-5.5:
|
|
129
225
|
|
|
130
226
|
```yaml
|
|
131
227
|
profiles:
|
|
@@ -134,13 +230,13 @@ profiles:
|
|
|
134
230
|
model: claude-opus-4-8
|
|
135
231
|
reasoningEffort: high
|
|
136
232
|
permissions:
|
|
137
|
-
mode: auto
|
|
233
|
+
mode: auto # protected auto mode for the Claude Captain
|
|
138
234
|
claude-opus-1m:
|
|
139
235
|
adapter: claude
|
|
140
236
|
model: claude-opus-4-8[1m]
|
|
141
237
|
reasoningEffort: xhigh
|
|
142
238
|
permissions:
|
|
143
|
-
mode: auto
|
|
239
|
+
mode: auto # protected auto mode for the Claude Coder
|
|
144
240
|
codex-gpt:
|
|
145
241
|
adapter: codex
|
|
146
242
|
model: gpt-5.5
|
|
@@ -148,19 +244,27 @@ profiles:
|
|
|
148
244
|
permissions:
|
|
149
245
|
mode: auto
|
|
150
246
|
writablePaths:
|
|
151
|
-
- .git
|
|
247
|
+
- .git # allow git metadata writes under Codex auto mode
|
|
152
248
|
|
|
153
249
|
captain: claude-opus
|
|
154
250
|
|
|
155
251
|
playbooks:
|
|
156
252
|
code:
|
|
157
|
-
from:
|
|
253
|
+
from: '@sublang/playbook/code/registry'
|
|
158
254
|
players:
|
|
159
255
|
coder: claude-opus-1m
|
|
160
256
|
reviewer: codex-gpt
|
|
161
|
-
committer: coder
|
|
257
|
+
committer: coder # which role commits — `coder` or `reviewer`
|
|
162
258
|
```
|
|
163
259
|
|
|
260
|
+
`committer` is CODE's one option: an alias naming which role runs the
|
|
261
|
+
commit turn (fallback semantics:
|
|
262
|
+
[PBRT-8](specs/dev/playbook-runtime.md#pbrt-8)). Each role's per-run
|
|
263
|
+
prompt names its pinned `model`, else its `adapter`
|
|
264
|
+
([PBRT-4](specs/user/playbook-runtime.md#pbrt-4)), so commit trailers
|
|
265
|
+
can credit the concrete model (e.g. `claude-opus-4-8[1m]`) rather than
|
|
266
|
+
the adapter family.
|
|
267
|
+
|
|
164
268
|
If you need a separate config file for a one-off run, pass a raw
|
|
165
269
|
`tmux-play` config explicitly; this bypasses the seed, composition, and
|
|
166
270
|
readiness gate and forwards the arguments to `tmux-play` verbatim
|
|
@@ -172,33 +276,23 @@ playbook --config ./tmux-play.config.yaml
|
|
|
172
276
|
|
|
173
277
|
### Install (contributors / from source)
|
|
174
278
|
|
|
175
|
-
|
|
279
|
+
The checked-in lockfile resolves every runtime contract required by the
|
|
280
|
+
source tree:
|
|
176
281
|
|
|
177
282
|
```sh
|
|
178
283
|
git clone https://github.com/sublang-ai/playbook.git
|
|
179
284
|
cd playbook
|
|
180
|
-
pnpm install
|
|
285
|
+
pnpm install --frozen-lockfile
|
|
181
286
|
pnpm build
|
|
182
287
|
pnpm test
|
|
183
288
|
```
|
|
184
289
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
release (see [RELEASE-14](specs/dev/release.md#release-14)). To
|
|
192
|
-
refresh the contributor pin within that range, run
|
|
193
|
-
`pnpm update @sublang/cligent` and commit the resulting
|
|
194
|
-
`pnpm-lock.yaml` change. To adopt a later cligent minor, update the
|
|
195
|
-
`package.json` specifier and lockfile together.
|
|
196
|
-
No local link required for any of this. To point pnpm at a local
|
|
197
|
-
`cligent` checkout
|
|
198
|
-
instead, copy
|
|
199
|
-
[`pnpm-workspace.yaml.example`](pnpm-workspace.yaml.example)
|
|
200
|
-
into place; the override is gitignored so it never leaks into a
|
|
201
|
-
production install.
|
|
290
|
+
To co-develop Playbook with an unreleased cligent checkout, clone
|
|
291
|
+
cligent beside this repository, build it, and copy
|
|
292
|
+
`pnpm-workspace.yaml.example` to the gitignored
|
|
293
|
+
`pnpm-workspace.yaml`. That optional override links the sibling checkout
|
|
294
|
+
in place of the registry version. Do not commit the local lockfile
|
|
295
|
+
rewrite it produces ([RELEASE-11](specs/dev/release.md#release-11)).
|
|
202
296
|
|
|
203
297
|
Drive a Boss turn against the source tree with the launcher, which
|
|
204
298
|
resolves `tmux-play`, the Playbook Captain shell, and the CODE registry
|
|
@@ -218,87 +312,33 @@ globally.
|
|
|
218
312
|
|
|
219
313
|
The Boss pane starts at the Playbook Captain shell. Use `/code <task>`
|
|
220
314
|
to explicitly select the CODE playbook, or use ordinary text and let the
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
315
|
+
compiled default Captain ask a material routing question or plan one or more
|
|
316
|
+
enabled playbook calls. It cannot answer the initial intent directly;
|
|
317
|
+
calls run sequentially so Captain can reassess after every child result
|
|
318
|
+
and then return a concrete result or actionable conclusion. Once a turn
|
|
319
|
+
reaches CODE, the CODE judge classifies it into an FSM event (start a coding
|
|
320
|
+
turn, continue or summarize an iteration, interrupt to a named state, or
|
|
321
|
+
nothing) per
|
|
224
322
|
[PBRT-1](specs/user/playbook-runtime.md#pbrt-1).
|
|
225
323
|
When a player surfaces a clarifying question
|
|
226
|
-
the FSM parks
|
|
227
|
-
next turn is normally classified as the reply
|
|
324
|
+
the FSM parks, the pane shows the question, and your
|
|
325
|
+
next turn is normally classified as the reply — or a fresh directive
|
|
228
326
|
abandons it ([PBRT-2](specs/user/playbook-runtime.md#pbrt-2)).
|
|
229
327
|
|
|
230
328
|
The Captain pane shows `/code` start/stop/finished status with `◇` lines
|
|
231
|
-
and streams
|
|
232
|
-
[PBRT-3](specs/user/playbook-runtime.md#pbrt-3), while player
|
|
233
|
-
ride their own panes.
|
|
234
|
-
|
|
235
|
-
Published configs import the shell adapter from
|
|
236
|
-
`@sublang/playbook/playbook-captain` and enable CODE through a
|
|
237
|
-
`captain.options.playbooks.code` block whose `from` is
|
|
238
|
-
`@sublang/playbook/code/registry`. The generic `playbook` launcher
|
|
239
|
-
composes this for you from the top-level `profiles` / `playbooks`
|
|
240
|
-
config above.
|
|
329
|
+
and streams CODE progress with captain-speech classification/questions
|
|
330
|
+
per [PBRT-3](specs/user/playbook-runtime.md#pbrt-3), while player
|
|
331
|
+
prompts ride their own panes.
|
|
241
332
|
|
|
242
333
|
### Embedding the runtime in your own host
|
|
243
334
|
|
|
244
|
-
The runtime is host-agnostic
|
|
245
|
-
|
|
246
|
-
[`@sublang/playbook/runtime`](src/runtime.ts)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
`@sublang/playbook/code/playbook`; `PlaybookRuntimeFactory` is available
|
|
252
|
-
from `@sublang/playbook/runtime`.
|
|
253
|
-
Construct the runtime against your own ports:
|
|
254
|
-
|
|
255
|
-
```ts
|
|
256
|
-
import createPlaybookRuntime from '@sublang/playbook/code/playbook';
|
|
257
|
-
import type { PlaybookPorts } from '@sublang/playbook/runtime';
|
|
258
|
-
|
|
259
|
-
const ports: PlaybookPorts = {
|
|
260
|
-
callPlayer: async (playerId, prompt, signal) => { /* … */ },
|
|
261
|
-
callJudge: async (prompt, signal) => { /* … */ },
|
|
262
|
-
emitStatus: async (message, data) => { /* … */ },
|
|
263
|
-
emitTelemetry: async ({ topic, payload }) => { /* … */ },
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const runtime = createPlaybookRuntime({
|
|
267
|
-
coderPlayer: 'claude',
|
|
268
|
-
reviewerPlayer: 'codex',
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
await runtime.init(ports);
|
|
272
|
-
await runtime.handleBossInput({
|
|
273
|
-
text: 'Start fixing the bug',
|
|
274
|
-
signal: new AbortController().signal,
|
|
275
|
-
});
|
|
276
|
-
await runtime.dispose();
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
See
|
|
280
|
-
[`code.playbook.test.ts`](reference/sdlc/code.playbook/code.playbook.test.ts)
|
|
281
|
-
for the full range of port shapes (classifier, judge, abort, interrupt,
|
|
282
|
-
status/telemetry) the runtime is contract-tested against.
|
|
283
|
-
|
|
284
|
-
### Reading the published spec contracts
|
|
285
|
-
|
|
286
|
-
The authored compiler-phase specs ship in the package and are exposed
|
|
287
|
-
as a public, semver-stable surface under `@sublang/playbook/slc/*`.
|
|
288
|
-
Resolve and read one with `import.meta.resolve` plus `fs`:
|
|
289
|
-
|
|
290
|
-
```ts
|
|
291
|
-
import { readFile } from 'node:fs/promises';
|
|
292
|
-
import { fileURLToPath } from 'node:url';
|
|
293
|
-
|
|
294
|
-
const url = import.meta.resolve('@sublang/playbook/slc/link.md');
|
|
295
|
-
const link = await readFile(fileURLToPath(url), 'utf8');
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
The three specs are [`slc/text2gears.md`](slc/text2gears.md),
|
|
299
|
-
[`slc/gears2fsm.md`](slc/gears2fsm.md), and [`slc/link.md`](slc/link.md)
|
|
300
|
-
— the FSM-to-runtime contract that `@sublang/playbook/runtime` projects
|
|
301
|
-
into TypeScript.
|
|
335
|
+
The runtime is host-agnostic — the `tmux-play` adapter is one host. The
|
|
336
|
+
port and runtime contracts live in the type-only, semver-stable module
|
|
337
|
+
[`@sublang/playbook/runtime`](src/runtime.ts): a host satisfies the six
|
|
338
|
+
ports once and inherits every playbook. See
|
|
339
|
+
[docs/embedding.md](docs/embedding.md) for the contract surface, a
|
|
340
|
+
complete ports example, session/trace semantics, and how to read the
|
|
341
|
+
published compiler-phase specs from the package.
|
|
302
342
|
|
|
303
343
|
## Workflow
|
|
304
344
|
|
|
@@ -306,33 +346,25 @@ playbook is itself spec-driven: the compiler phases are specs in `slc/`,
|
|
|
306
346
|
and the reference playbook is regenerated from its prose source. The
|
|
307
347
|
loop:
|
|
308
348
|
|
|
309
|
-
1. **Edit source.**
|
|
310
|
-
[`reference/sdlc/code.md`](reference/sdlc/code.md)
|
|
349
|
+
1. **Edit source.** The worked examples include
|
|
350
|
+
[`reference/sdlc/code.md`](reference/sdlc/code.md) and the generic
|
|
351
|
+
[`reference/sdlc/captain.md`](reference/sdlc/captain.md).
|
|
311
352
|
2. **Recompile gears** per [`slc/text2gears.md`](slc/text2gears.md) into
|
|
312
|
-
the
|
|
313
|
-
3. **Recompile FSM** per
|
|
314
|
-
|
|
353
|
+
the source's `<name>.playbook/<name>.gears.md`.
|
|
354
|
+
3. **Recompile FSM and runtime** per
|
|
355
|
+
[`slc/gears2fsm.md`](slc/gears2fsm.md) and
|
|
356
|
+
[`slc/link.md`](slc/link.md) into that playbook artifact directory.
|
|
315
357
|
4. **Sync runtime, tests, and downstream specs** so `pnpm test` stays
|
|
316
358
|
green and the introspect contract holds 1:1 between gear items and
|
|
317
|
-
FSM
|
|
359
|
+
FSM direct-Captain, delegated-player, and nested-playbook states.
|
|
318
360
|
5. **Commit** with co-author trailers per
|
|
319
361
|
[`specs/dev/git.md`](specs/dev/git.md).
|
|
320
362
|
|
|
321
363
|
The behavioral contract between gears and FSM
|
|
322
364
|
([PLAYBOOK-1..6](specs/dev/playbook.md)) and the runtime contract that
|
|
323
|
-
ports satisfy ([PBRT
|
|
324
|
-
in [`specs/dev/`](specs/dev/) and verified by tests under the
|
|
325
|
-
package.
|
|
326
|
-
|
|
327
|
-
## Requirements
|
|
328
|
-
|
|
329
|
-
- Node.js ≥ 20.6.0 (the `playbook` launcher uses
|
|
330
|
-
`import.meta.resolve`, unflagged since this release)
|
|
331
|
-
- pnpm 9 (for the reference package)
|
|
332
|
-
- A configured `tmux-play` host (for live Boss turns) — requires
|
|
333
|
-
`tmux` and [`glow`](https://github.com/charmbracelet/glow#installation)
|
|
334
|
-
on `PATH`; cligent 0.4+ uses `glow` to render Markdown pane
|
|
335
|
-
output and fails fast without it
|
|
365
|
+
ports satisfy ([the PBRT dev items](specs/dev/playbook-runtime.md)) are
|
|
366
|
+
pinned in [`specs/dev/`](specs/dev/) and verified by tests under the
|
|
367
|
+
reference package.
|
|
336
368
|
|
|
337
369
|
## Contributing
|
|
338
370
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sublang/playbook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Composable XState v5 playbook runtime with compiled Captain, CODE, and DISCUSS workflows driven by GEARS specs.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"homepage": "https://github.com/sublang-ai/playbook#readme",
|
|
8
8
|
"repository": {
|
|
@@ -28,9 +28,21 @@
|
|
|
28
28
|
"src/runtime.ts",
|
|
29
29
|
"src/runtime.js",
|
|
30
30
|
"src/runtime.d.ts",
|
|
31
|
+
"src/xstate-runtime.ts",
|
|
32
|
+
"src/xstate-runtime.js",
|
|
33
|
+
"src/xstate-runtime.d.ts",
|
|
31
34
|
"slc/link.md",
|
|
32
35
|
"slc/gears2fsm.md",
|
|
33
36
|
"slc/text2gears.md",
|
|
37
|
+
"slc/optimize.md",
|
|
38
|
+
"reference/sdlc/captain.md",
|
|
39
|
+
"reference/sdlc/captain.playbook/captain.gears.md",
|
|
40
|
+
"reference/sdlc/captain.playbook/captain.fsm.ts",
|
|
41
|
+
"reference/sdlc/captain.playbook/captain.fsm.js",
|
|
42
|
+
"reference/sdlc/captain.playbook/captain.fsm.d.ts",
|
|
43
|
+
"reference/sdlc/captain.playbook/captain.playbook.ts",
|
|
44
|
+
"reference/sdlc/captain.playbook/captain.playbook.js",
|
|
45
|
+
"reference/sdlc/captain.playbook/captain.playbook.d.ts",
|
|
34
46
|
"reference/sdlc/code.playbook/code.fsm.ts",
|
|
35
47
|
"reference/sdlc/code.playbook/code.fsm.js",
|
|
36
48
|
"reference/sdlc/code.playbook/code.fsm.d.ts",
|
|
@@ -48,7 +60,18 @@
|
|
|
48
60
|
"reference/sdlc/code.playbook/playbook-captain.d.ts",
|
|
49
61
|
"reference/sdlc/code.playbook/code.gears.md",
|
|
50
62
|
"reference/sdlc/code.playbook/playbook.config.template.yaml",
|
|
51
|
-
"reference/sdlc/code.playbook/bin/playbook.js"
|
|
63
|
+
"reference/sdlc/code.playbook/bin/playbook.js",
|
|
64
|
+
"reference/sdlc/code.playbook/bin/run.js",
|
|
65
|
+
"reference/sdlc/discuss.playbook/discuss.gears.md",
|
|
66
|
+
"reference/sdlc/discuss.playbook/discuss.fsm.ts",
|
|
67
|
+
"reference/sdlc/discuss.playbook/discuss.fsm.js",
|
|
68
|
+
"reference/sdlc/discuss.playbook/discuss.fsm.d.ts",
|
|
69
|
+
"reference/sdlc/discuss.playbook/discuss.playbook.ts",
|
|
70
|
+
"reference/sdlc/discuss.playbook/discuss.playbook.js",
|
|
71
|
+
"reference/sdlc/discuss.playbook/discuss.playbook.d.ts",
|
|
72
|
+
"reference/sdlc/discuss.playbook/discuss.registry.ts",
|
|
73
|
+
"reference/sdlc/discuss.playbook/discuss.registry.js",
|
|
74
|
+
"reference/sdlc/discuss.playbook/discuss.registry.d.ts"
|
|
52
75
|
],
|
|
53
76
|
"bin": {
|
|
54
77
|
"playbook": "reference/sdlc/code.playbook/bin/playbook.js"
|
|
@@ -58,6 +81,14 @@
|
|
|
58
81
|
"types": "./src/runtime.d.ts",
|
|
59
82
|
"default": "./src/runtime.js"
|
|
60
83
|
},
|
|
84
|
+
"./xstate-runtime": {
|
|
85
|
+
"types": "./src/xstate-runtime.d.ts",
|
|
86
|
+
"default": "./src/xstate-runtime.js"
|
|
87
|
+
},
|
|
88
|
+
"./captain/playbook": {
|
|
89
|
+
"types": "./reference/sdlc/captain.playbook/captain.playbook.d.ts",
|
|
90
|
+
"default": "./reference/sdlc/captain.playbook/captain.playbook.js"
|
|
91
|
+
},
|
|
61
92
|
"./code/playbook": {
|
|
62
93
|
"types": "./reference/sdlc/code.playbook/code.playbook.d.ts",
|
|
63
94
|
"default": "./reference/sdlc/code.playbook/code.playbook.js"
|
|
@@ -70,7 +101,15 @@
|
|
|
70
101
|
"types": "./reference/sdlc/code.playbook/playbook-captain.d.ts",
|
|
71
102
|
"default": "./reference/sdlc/code.playbook/playbook-captain.js"
|
|
72
103
|
},
|
|
73
|
-
"./slc/*": "./slc/*"
|
|
104
|
+
"./slc/*": "./slc/*",
|
|
105
|
+
"./discuss/playbook": {
|
|
106
|
+
"types": "./reference/sdlc/discuss.playbook/discuss.playbook.d.ts",
|
|
107
|
+
"default": "./reference/sdlc/discuss.playbook/discuss.playbook.js"
|
|
108
|
+
},
|
|
109
|
+
"./discuss/registry": {
|
|
110
|
+
"types": "./reference/sdlc/discuss.playbook/discuss.registry.d.ts",
|
|
111
|
+
"default": "./reference/sdlc/discuss.playbook/discuss.registry.js"
|
|
112
|
+
}
|
|
74
113
|
},
|
|
75
114
|
"scripts": {
|
|
76
115
|
"build": "tsc",
|
|
@@ -82,9 +121,10 @@
|
|
|
82
121
|
"provenance": true
|
|
83
122
|
},
|
|
84
123
|
"dependencies": {
|
|
85
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
124
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.154",
|
|
86
125
|
"@openai/codex-sdk": "^0.139.0",
|
|
87
|
-
"@sublang/cligent": "^0.
|
|
126
|
+
"@sublang/cligent": "^0.15.0",
|
|
127
|
+
"p-queue": "^9.3.1",
|
|
88
128
|
"xstate": "^5.19.4",
|
|
89
129
|
"yaml": "^2.9.0"
|
|
90
130
|
},
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
|
2
|
+
<!-- SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai> -->
|
|
3
|
+
|
|
4
|
+
# Captain
|
|
5
|
+
|
|
6
|
+
This is the default generic Captain playbook.
|
|
7
|
+
It has no players beyond Boss and Captain.
|
|
8
|
+
|
|
9
|
+
At runtime Captain receives the exact original Boss intent and a catalog of enabled callable playbooks.
|
|
10
|
+
Each catalog entry contains only a stable playbook id, its command, and its intent.
|
|
11
|
+
The catalog is immutable host input for the session; Boss events and Captain decisions cannot replace it.
|
|
12
|
+
Captain shall call only ids in that catalog and shall never call this Captain playbook itself.
|
|
13
|
+
Captain is a router, not the specialist that performs the requested work.
|
|
14
|
+
Captain shall decide only from the supplied Boss text and catalog, without investigating the task, inspecting the workspace, using tools, or relying on ambient project evidence.
|
|
15
|
+
Captain shall keep a finite ordered plan and issue at most one child call at a time.
|
|
16
|
+
`remainingPlan` shall contain only calls after the selected next call; every continuation shall strictly reduce its length.
|
|
17
|
+
After Captain consumes an answer to its own routing or reassessment question,
|
|
18
|
+
that question and answer are no longer pending before Captain calls a child or
|
|
19
|
+
completes.
|
|
20
|
+
|
|
21
|
+
The host guarantees that this Captain receives Boss input only while it is the active leaf and resumes only from a matching child return.
|
|
22
|
+
That guarantee is an execution precondition, not a behavior for Captain to perform and not a source item to compile.
|
|
23
|
+
An ordinary non-empty Boss turn at the ready state enters as a new intent with the exact Boss text and does not require model classification.
|
|
24
|
+
When parked work can accept either an answer or a fresh directive, a classifier may select only the event kind and routing metadata; the runtime attaches the exact Boss text as the answer or fresh intent.
|
|
25
|
+
These input-provenance rules are linker preconditions, not behaviors for Captain to perform and not source items to compile.
|
|
26
|
+
|
|
27
|
+
When Boss gives a new intent while Captain is the active playbook, Captain shall ask Boss one material routing question or select the first call in a one- or multi-playbook plan, using the following prompt:
|
|
28
|
+
> Boss intent: <boss-intent>
|
|
29
|
+
> Enabled playbooks: <enabled-playbooks>
|
|
30
|
+
> You are routing this intent, not performing the requested work.
|
|
31
|
+
> Use only the Boss intent and enabled-playbooks catalog supplied here.
|
|
32
|
+
> Do not investigate the task, inspect files or project state, use tools, or attempt the specialized work yourself.
|
|
33
|
+
> Preserve Boss's intended outcome and constraints.
|
|
34
|
+
> If the supplied evidence identifies a useful route, select an enabled playbook; do not finish the intent yourself.
|
|
35
|
+
> Ask exactly one concise question only when its answer is necessary to choose a useful route or call order.
|
|
36
|
+
> For a complex intent, divide it into the smallest finite ordered plan of useful playbook calls.
|
|
37
|
+
> Name the selected first playbook and state its complete standalone request containing only the context it needs.
|
|
38
|
+
> List any later playbook calls in their intended order after the selected first call.
|
|
39
|
+
> Do not call a playbook merely to restate or classify the intent.
|
|
40
|
+
> Write only concise human-facing routing prose or the one routing question.
|
|
41
|
+
> Do not emit JSON, guard names, result property names, or control instructions.
|
|
42
|
+
> Do not expose internal state ids, session ids, call ids, stack data, hidden control data, or private reasoning.
|
|
43
|
+
|
|
44
|
+
Results:
|
|
45
|
+
- `question`: Captain asked the one material routing question. Output shall include `question: <verbatim final text from the visible Captain call>`.
|
|
46
|
+
- `delegation`: Captain selected the first useful call. Output shall include `remainingPlan: <finite JSON-safe array of only later calls>`, `nextPlaybookId: <selected stable enabled-playbook id>`, and `nextPlaybookInput: <complete standalone request>`.
|
|
47
|
+
|
|
48
|
+
A fresh directive that interrupts any parked Captain work shall restart this
|
|
49
|
+
routing behavior with that fresh intent. It shall not jump directly into
|
|
50
|
+
reassessment or retain the prior question, answer, plan, call history,
|
|
51
|
+
evidence, selection, response, or error.
|
|
52
|
+
|
|
53
|
+
A question decision shall carry one concise `question` and wait for Boss without losing the original intent.
|
|
54
|
+
Boss's answer resumes this same routing decision with continuation context; it is not a separate Captain behavior.
|
|
55
|
+
A delegation decision shall carry a finite `remainingPlan` plus non-empty `nextPlaybookId` and `nextPlaybookInput` for its first call.
|
|
56
|
+
The compiled routing result guards are exactly `question` and `delegation`, respectively; these names are part of this default playbook's stable machine contract.
|
|
57
|
+
The initial routing state has no direct or terminal outcome.
|
|
58
|
+
|
|
59
|
+
When Captain selects a next call with a non-empty `nextPlaybookId` from the enabled catalog, Captain shall call playbook selected by `nextPlaybookId`:
|
|
60
|
+
> <nextPlaybookInput>
|
|
61
|
+
|
|
62
|
+
When the called playbook returns successfully, aborts, or fails, Captain shall reassess the original intent, remaining plan, and completed call results using the following prompt:
|
|
63
|
+
> Boss intent: <boss-intent>
|
|
64
|
+
> Enabled playbooks: <enabled-playbooks>
|
|
65
|
+
> Remaining plan: <remaining-plan>
|
|
66
|
+
> Completed call results: <completed-call-results>
|
|
67
|
+
> Preserve Boss's intended outcome and constraints.
|
|
68
|
+
> Treat each returned result as evidence and revise the remaining plan when needed.
|
|
69
|
+
> A continuing decision must strictly reduce the remaining plan length.
|
|
70
|
+
> Do not repeat an equivalent failed or completed call without new information.
|
|
71
|
+
> If the intent is fulfilled, give Boss one concise final response that states the result or actionable conclusion.
|
|
72
|
+
> Do not finish with a bare acknowledgement, a promise to act, or an announcement that the round is complete.
|
|
73
|
+
> If information from Boss is now necessary, ask exactly one concise question.
|
|
74
|
+
> Otherwise name exactly one next enabled playbook and state its complete standalone request containing only the context it needs.
|
|
75
|
+
> List any still-later playbook calls in their intended order after the selected next call.
|
|
76
|
+
> Write only concise human-facing final, question, or routing prose.
|
|
77
|
+
> Do not emit JSON, guard names, result property names, or control instructions.
|
|
78
|
+
> Do not expose internal state ids, session ids, call ids, stack data, hidden control data, or private reasoning.
|
|
79
|
+
|
|
80
|
+
Results:
|
|
81
|
+
- `final`: Captain gave Boss the concrete result or actionable conclusion. Output shall include `response: <verbatim final text from the visible Captain call>`.
|
|
82
|
+
- `followUpQuestion`: Captain asked one necessary follow-up question. Output shall include `question: <verbatim final text from the visible Captain call>`.
|
|
83
|
+
- `continuing`: Captain selected another useful call. Output shall include `remainingPlan: <strictly shorter finite JSON-safe array of only later calls>`, `nextPlaybookId: <selected stable enabled-playbook id>`, and `nextPlaybookInput: <complete standalone request>`.
|
|
84
|
+
|
|
85
|
+
For the machine's deterministic safety floor, two calls are the same only when
|
|
86
|
+
both the stable target id and complete standalone input match exactly.
|
|
87
|
+
Captain shall record that exact pair before invoking the child, so an `ok`,
|
|
88
|
+
`aborted`, or `error` return all prevent the same later attempt. An input
|
|
89
|
+
revised with new information is different for this exact check; Captain still
|
|
90
|
+
owns the broader semantic no-repeat instruction above.
|
|
91
|
+
|
|
92
|
+
Each completed call result shall contain only the selected playbook id, its `ok`, `aborted`, or `error` status, and either the child's actual JSON-safe output or a compact error with only `name` and `message`.
|
|
93
|
+
It shall never retain or expose a child session id, call id, child state, stack trace, or an opaque runtime result object.
|
|
94
|
+
|
|
95
|
+
A final decision shall carry a concise JSON-safe `response` and complete.
|
|
96
|
+
A follow-up question shall carry one concise `question` and wait for Boss without losing the original intent, plan, or completed results.
|
|
97
|
+
Boss's answer resumes this same reassessment with continuation context; it is not a separate Captain behavior.
|
|
98
|
+
A continuing decision shall carry a strictly shorter finite `remainingPlan` plus non-empty `nextPlaybookId` and `nextPlaybookInput`.
|
|
99
|
+
The compiled reassessment result guards are exactly `final`,
|
|
100
|
+
`followUpQuestion`, and `continuing`, respectively; these names are part of
|
|
101
|
+
this default playbook's stable machine contract.
|
|
102
|
+
A child abort or failure is a completed call result for reassessment and shall not route this playbook directly to its generic failure state.
|