@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.
Files changed (48) hide show
  1. package/README.md +183 -151
  2. package/package.json +46 -6
  3. package/reference/sdlc/captain.md +102 -0
  4. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +227 -0
  5. package/reference/sdlc/captain.playbook/captain.fsm.js +628 -0
  6. package/reference/sdlc/captain.playbook/captain.fsm.ts +851 -0
  7. package/reference/sdlc/captain.playbook/captain.gears.md +60 -0
  8. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +23 -0
  9. package/reference/sdlc/captain.playbook/captain.playbook.js +1053 -0
  10. package/reference/sdlc/captain.playbook/captain.playbook.ts +1144 -0
  11. package/reference/sdlc/code.playbook/bin/playbook.js +152 -10
  12. package/reference/sdlc/code.playbook/bin/run.js +893 -0
  13. package/reference/sdlc/code.playbook/code.fsm.d.ts +11 -4
  14. package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +2 -2
  15. package/reference/sdlc/code.playbook/code.fsm.introspect.js +1 -1
  16. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +6 -6
  17. package/reference/sdlc/code.playbook/code.fsm.js +334 -102
  18. package/reference/sdlc/code.playbook/code.fsm.ts +467 -180
  19. package/reference/sdlc/code.playbook/code.gears.md +11 -10
  20. package/reference/sdlc/code.playbook/code.playbook.d.ts +18 -9
  21. package/reference/sdlc/code.playbook/code.playbook.js +1095 -200
  22. package/reference/sdlc/code.playbook/code.playbook.ts +1437 -256
  23. package/reference/sdlc/code.playbook/code.registry.d.ts +0 -3
  24. package/reference/sdlc/code.playbook/code.registry.js +0 -3
  25. package/reference/sdlc/code.playbook/code.registry.ts +0 -6
  26. package/reference/sdlc/code.playbook/playbook-captain.d.ts +9 -4
  27. package/reference/sdlc/code.playbook/playbook-captain.js +889 -210
  28. package/reference/sdlc/code.playbook/playbook-captain.ts +1136 -257
  29. package/reference/sdlc/code.playbook/playbook.config.template.yaml +10 -0
  30. package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +396 -0
  31. package/reference/sdlc/discuss.playbook/discuss.fsm.js +2066 -0
  32. package/reference/sdlc/discuss.playbook/discuss.fsm.ts +2464 -0
  33. package/reference/sdlc/discuss.playbook/discuss.gears.md +251 -0
  34. package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +113 -0
  35. package/reference/sdlc/discuss.playbook/discuss.playbook.js +1514 -0
  36. package/reference/sdlc/discuss.playbook/discuss.playbook.ts +1926 -0
  37. package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +58 -0
  38. package/reference/sdlc/discuss.playbook/discuss.registry.js +97 -0
  39. package/reference/sdlc/discuss.playbook/discuss.registry.ts +153 -0
  40. package/slc/gears2fsm.md +557 -57
  41. package/slc/link.md +1097 -80
  42. package/slc/optimize.md +88 -0
  43. package/slc/text2gears.md +247 -5
  44. package/src/runtime.d.ts +145 -3
  45. package/src/runtime.ts +200 -2
  46. package/src/xstate-runtime.d.ts +94 -0
  47. package/src/xstate-runtime.js +1247 -0
  48. package/src/xstate-runtime.ts +1802 -0
package/README.md CHANGED
@@ -7,28 +7,49 @@
7
7
  [![Node.js](https://img.shields.io/node/v/@sublang/playbook)](https://nodejs.org/)
8
8
  [![CI](https://github.com/sublang-ai/playbook/actions/workflows/ci.yml/badge.svg)](https://github.com/sublang-ai/playbook/actions/workflows/ci.yml)
9
9
 
10
- *Skills made reliable through state machines and visualization.*
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 *playbook* — that orchestrates other AI agents (players) per a
15
- spec written in plain prose. Three phases take prose to runtime:
16
-
17
- 1. **text GEARS** ([slc/text2gears.md](slc/text2gears.md)) normative
18
- spec items, one per state behavior, partitioned by trigger and prompt
19
- content.
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 captain-invoking state
22
- with a typed Captain actor contract.
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 is itself an end-to-end worked example: this
28
- package — `@sublang/playbook` — is the SDLC coding workflow,
29
- generated from [`reference/sdlc/code.md`](reference/sdlc/code.md)
30
- as its prose source. The runtime drives a coder / reviewer /
31
- committer loop end to end.
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 `<basename>.<pipeline>/` output directory.
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` Judge agent, optional `layout` /
104
- `notifications` / `theme`, and a `playbooks` map of enabled playbooks. Each
105
- `captain` or `players.<role>` value is a profile id or an adapter
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 `coder` /
110
- `reviewer` player roles that reference them. Within a `playbooks.<id>` block, `from` (the registry module),
111
- `command` (an optional slash-command override), and `players` are
112
- launcher-owned; every other key (e.g. CODE's `committer`) is that
113
- playbook's option slice. The launcher injects `captain.from` and the
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, with the Committer aliased to the Coder:
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 # protected auto mode for the Claude Captain
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 # protected auto mode for the Claude Coder
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 # allow git metadata writes under Codex auto mode
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: "@sublang/playbook/code/registry"
253
+ from: '@sublang/playbook/code/registry'
158
254
  players:
159
255
  coder: claude-opus-1m
160
256
  reviewer: codex-gpt
161
- committer: coder # which role commits — `coder` or `reviewer`
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
- Clone, install, and run the suite locally:
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
- `pnpm install` here installs the `@sublang/cligent` version pinned
186
- in the checked-in `pnpm-lock.yaml` the same version CI installs
187
- via `--frozen-lockfile`, so contributor checkouts and CI agree.
188
- The published `package.json` declares `@sublang/cligent` as
189
- `^0.13.0`, so an end-user install with no lockfile (e.g., `npm
190
- install -g @sublang/playbook`) resolves a compatible cligent 0.13.x
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
- shell route it. Once a turn reaches CODE, the CODE judge classifies it
222
- into an FSM event (start a coding turn, continue or summarize an IR,
223
- interrupt to a named state, or nothing) per
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 at `awaitBossReply` and the pane shows the question; your
227
- next turn is normally classified as the reply, or a fresh directive
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 the CODE state machine with the four-glyph vocabulary `◆ ▸ ⮕ ⤷` per
232
- [PBRT-3](specs/user/playbook-runtime.md#pbrt-3), while player prompts
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; the `tmux-play` adapter is one host.
245
- The port and runtime contracts live in the type-only module
246
- [`@sublang/playbook/runtime`](src/runtime.ts) a public, semver-stable
247
- surface (`PlayerResult`, `PlaybookPorts`, `PlaybookRuntime`,
248
- `PlaybookRuntimeFactory`) that imports no CODE or FSM types, so a host
249
- satisfies it once and inherits every playbook. The CODE runtime
250
- re-exports `PlayerResult`, `PlaybookPorts`, and `PlaybookRuntime` from
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.** For the reference, that's
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 package's `code.gears.md`.
313
- 3. **Recompile FSM** per [`slc/gears2fsm.md`](slc/gears2fsm.md) into
314
- the package's `code.fsm.ts`.
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 captain-invoking states.
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-5..16](specs/dev/playbook-runtime.md)) are pinned
324
- in [`specs/dev/`](specs/dev/) and verified by tests under the reference
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.9.0",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
- "description": "Reference CODE playbook — XState v5 FSM, host-agnostic runtime, and Playbook Captain shell for a coder/reviewer/committer loop driven by GEARS spec items.",
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.143",
124
+ "@anthropic-ai/claude-agent-sdk": "^0.3.154",
86
125
  "@openai/codex-sdk": "^0.139.0",
87
- "@sublang/cligent": "^0.13.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.