@sublang/playbook 10.0.0 → 12.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 (59) hide show
  1. package/README.md +1 -1
  2. package/docs/cli.md +67 -19
  3. package/docs/configuration.md +103 -40
  4. package/docs/embedding.md +88 -0
  5. package/package.json +30 -4
  6. package/reference/sdlc/code.md +35 -15
  7. package/reference/sdlc/code.playbook/bin/interactive-session.js +58 -6
  8. package/reference/sdlc/code.playbook/bin/launch-config.js +499 -241
  9. package/reference/sdlc/code.playbook/bin/playbook.js +236 -187
  10. package/reference/sdlc/code.playbook/bin/replay-observer.js +221 -0
  11. package/reference/sdlc/code.playbook/bin/run.js +355 -203
  12. package/reference/sdlc/code.playbook/bin/session-store.js +1512 -136
  13. package/reference/sdlc/code.playbook/code.fsm.d.ts +22 -19
  14. package/reference/sdlc/code.playbook/code.fsm.js +116 -52
  15. package/reference/sdlc/code.playbook/code.fsm.ts +149 -64
  16. package/reference/sdlc/code.playbook/code.gears.md +40 -20
  17. package/reference/sdlc/code.playbook/code.playbook.js +23 -2
  18. package/reference/sdlc/code.playbook/code.playbook.ts +23 -2
  19. package/reference/sdlc/code.playbook/playbook-captain.d.ts +4 -1
  20. package/reference/sdlc/code.playbook/playbook-captain.js +21 -3
  21. package/reference/sdlc/code.playbook/playbook-captain.ts +42 -6
  22. package/reference/sdlc/code.playbook/playbook.config.template.yaml +31 -11
  23. package/reference/sdlc/code.playbook/session-store.d.ts +82 -0
  24. package/reference/sdlc/code.playbook/session-store.js +113 -0
  25. package/reference/sdlc/decide.md +24 -15
  26. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +13 -6
  27. package/reference/sdlc/decide.playbook/decide.fsm.js +54 -27
  28. package/reference/sdlc/decide.playbook/decide.fsm.ts +68 -29
  29. package/reference/sdlc/decide.playbook/decide.gears.md +25 -19
  30. package/reference/sdlc/decide.playbook/decide.playbook.js +11 -3
  31. package/reference/sdlc/decide.playbook/decide.playbook.ts +11 -3
  32. package/reference/sdlc/decide.playbook/decide.registry.js +1 -1
  33. package/reference/sdlc/decide.playbook/decide.registry.ts +1 -1
  34. package/reference/sdlc/dev.md +52 -0
  35. package/reference/sdlc/dev.playbook/dev.fsm.d.ts +261 -0
  36. package/reference/sdlc/dev.playbook/dev.fsm.js +723 -0
  37. package/reference/sdlc/dev.playbook/dev.fsm.ts +988 -0
  38. package/reference/sdlc/dev.playbook/dev.gears.md +91 -0
  39. package/reference/sdlc/dev.playbook/dev.playbook.d.ts +21 -0
  40. package/reference/sdlc/dev.playbook/dev.playbook.js +143 -0
  41. package/reference/sdlc/dev.playbook/dev.playbook.ts +246 -0
  42. package/reference/sdlc/dev.playbook/dev.registry.d.ts +40 -0
  43. package/reference/sdlc/dev.playbook/dev.registry.js +64 -0
  44. package/reference/sdlc/dev.playbook/dev.registry.ts +120 -0
  45. package/reference/sdlc/review.md +36 -18
  46. package/reference/sdlc/review.playbook/review.fsm.d.ts +15 -2
  47. package/reference/sdlc/review.playbook/review.fsm.js +77 -27
  48. package/reference/sdlc/review.playbook/review.fsm.ts +96 -30
  49. package/reference/sdlc/review.playbook/review.gears.md +52 -26
  50. package/reference/sdlc/review.playbook/review.playbook.js +17 -7
  51. package/reference/sdlc/review.playbook/review.playbook.ts +17 -7
  52. package/reference/sdlc/review.playbook/review.registry.js +1 -1
  53. package/reference/sdlc/review.playbook/review.registry.ts +1 -1
  54. package/slc/link.md +12 -5
  55. package/slc/text2gears.md +3 -0
  56. package/src/xstate-playbook-runtime.js +5 -2
  57. package/src/xstate-playbook-runtime.ts +5 -2
  58. package/src/xstate-runtime.js +13 -1
  59. package/src/xstate-runtime.ts +13 -1
package/README.md CHANGED
@@ -63,7 +63,7 @@ playbook
63
63
  Type a task, enter `/code <task>` for implementation, or enter
64
64
  `/decide <question>` for an independently proposed and reviewed decision.
65
65
 
66
- On first launch, Playbook writes its config to `${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook.config.yaml`.
66
+ On first launch, Playbook writes its config to `${SPEX_HOME:-$HOME/.spex}/playbook/playbook.config.yaml`. A config left at the former `${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook.config.yaml` is moved there on the next launch, unless a relative primary locator would change targets; that case is rejected unchanged with absolute replacements.
67
67
 
68
68
  The same config, compiled Captain, enabled playbooks, stable players, and
69
69
  nested calls power headless turns. Both front ends create the same durable
package/docs/cli.md CHANGED
@@ -105,8 +105,8 @@ its exit status or signal and exit `127` when it cannot be spawned
105
105
 
106
106
  The Boss pane starts at the Playbook Captain shell, where the session
107
107
  Captain runs for the whole session and sees every turn. Use `/code`,
108
- `/review`, or `/decide` followed by a task to select one of the bundled
109
- playbooks explicitly. A registered command resolves deterministically,
108
+ `/review`, `/decide`, or `/dev` followed by a task to select one of the
109
+ bundled playbooks explicitly. A registered command resolves deterministically,
110
110
  with no model call parsing it: at idle it starts that playbook, at its
111
111
  own leaf it delivers the rest of the line, an enabled command absent
112
112
  from the active path switches to it, and a bare command answers with
@@ -121,9 +121,11 @@ untouched
121
121
  ([[playbook-captain-1](https://github.com/sublang-ai/playbook/blob/main/specs/packages/playbook-captain.md#playbook-captain-1)],
122
122
  [[playbook-captain-2](https://github.com/sublang-ai/playbook/blob/main/specs/packages/playbook-captain.md#playbook-captain-2)]).
123
123
 
124
- The current CODE, REVIEW, and DECIDE workflows take their deterministic
124
+ The current CODE, REVIEW, DECIDE, and DEV workflows take their deterministic
125
125
  initial event from the selecting Boss turn. CODE and DECIDE then call REVIEW
126
- as a nested playbook. Local role names do not imply continuity: each frame
126
+ as a nested playbook, while DEV the repository-aware planner behind
127
+ `/dev` — analyzes a development request and itself calls CODE, or DECIDE and
128
+ then CODE, as nested playbooks. Local role names do not imply continuity: each frame
127
129
  uses the exact stable player IDs configured under its `roles` map. Equal IDs
128
130
  share one pane and provider conversation across nested and later root
129
131
  engagements; distinct IDs remain isolated even when their agent settings are
@@ -188,7 +190,7 @@ The former positional `<from>`, `resume`, `--player`, `--captain`,
188
190
  `--option`, `--cwd`, `--last`, run-only `--config`, and top-level `run:`
189
191
  config are removed from `playbook run`. Enable a registry under `playbooks`,
190
192
  declare provider agents once under top-level `players`, bind every local role
191
- under `playbooks.<id>.roles`, tune compatible model and effort in a `--with`
193
+ under `playbooks.<id>.roles`, tune compatible model, effort, and fast mode in a `--with`
192
194
  overlay, invoke the effective `/command`, and run from the working directory
193
195
  you want agents to use. Legacy `playbooks.<id>.players` blocks are rejected and
194
196
  are not auto-migrated because choosing equal or distinct new player IDs chooses
@@ -231,12 +233,13 @@ module's directory is a git repository, add `node_modules/` to its
231
233
 
232
234
  ### Continuing a Captain session
233
235
 
234
- Interactive and headless commands write the same logical-session records under
235
- `${XDG_STATE_HOME:-$HOME/.local/state}/playbook/sessions/`. A fresh interactive
236
- child persists turn zero before printing `playbook: session <id>` and opening
237
- Boss input; a fresh headless turn returns the same kind of ID in `--json`.
238
- After the current writer exits or explicitly hands off, either presentation
239
- can reopen either origin:
236
+ Interactive and headless commands write the same logical-session records in
237
+ the configured [`sessions` directory](configuration.md#session-storage), whose
238
+ default is `${XDG_STATE_HOME:-$HOME/.local/state}/playbook/sessions/`. A fresh
239
+ interactive child persists turn zero before printing `playbook: session <id>`
240
+ and opening Boss input; a fresh headless turn returns the same kind of ID in
241
+ `--json`. After the current writer exits or explicitly hands off, either
242
+ presentation can reopen either origin:
240
243
 
241
244
  ```sh
242
245
  # Reopen the newest session stored for this working directory headlessly:
@@ -265,20 +268,64 @@ competing front end fails closed instead of forking the history.
265
268
  An ordinary reopen reads current config and any opening `--with` fragments,
266
269
  projects them to the stored catalog and player roster, and requires the stored
267
270
  role bindings plus every structural setting to remain exact. Compatible
268
- current `model` and `effort` selections apply to the next call, including an
269
- explicit boolean `false` provider-default reset. The retained provider token
270
- is never silently replaced by a fresh conversation if that selection is not
271
- supported
271
+ current `model`, `effort`, and optional `fastMode` settings apply to the next
272
+ call. Boolean `false` explicitly resets model or effort to the provider
273
+ default, but `fastMode: false` is a literal disabled request; omission selects
274
+ the fast-mode provider default or inherits the player value at a role binding.
275
+ The retained provider token is never silently replaced by a fresh conversation
276
+ if a requested setting is not supported
272
277
  ([[playbook-cli-22](https://github.com/sublang-ai/playbook/blob/main/specs/packages/playbook-cli.md#playbook-cli-22)],
273
278
  [DR-032](https://github.com/sublang-ai/playbook/blob/main/specs/decisions/032-explicit-roles-session-players.md)).
274
279
 
280
+ ### Following replay history
281
+
282
+ Beside each `<session-id>.json` manifest, both front ends tee every observed
283
+ host record in order to `<session-id>.records.jsonl`. Each complete line is a
284
+ version-1 envelope with a writer-assigned sequence contiguous from `1`, the
285
+ opaque record, and an optional local playbook role where the trace establishes
286
+ one unambiguously. The stream is a mode-`0600` regular non-symlink file inside
287
+ the private sessions directory. Readers consume only the complete
288
+ newline-terminated prefix; a final partial line is not presented as a record.
289
+
290
+ The replay projection recursively removes provider `resumeToken` fields and
291
+ string-valued `resume` selections while preserving `resume: false`. It is
292
+ therefore useful for following and presentation, but it cannot resume an agent
293
+ conversation and does not replace the private canonical manifest. External
294
+ hosts can follow this stream without taking the writer lease through the
295
+ published [`@sublang/playbook/session-store`
296
+ facade](embedding.md#sharing-the-cli-session-store).
297
+
298
+ Streams are unbounded in this version: neither front end prunes them, and
299
+ retention remains tied to the deferred session-deletion policy. Removing
300
+ provider credentials does not make the remaining replay content non-sensitive;
301
+ protect the prompts, replies, tool calls, events, and timestamps it contains as
302
+ sensitive session data.
303
+
304
+ Replay recording is fail-soft after the canonical session lease is valid. If
305
+ initialization, sanitization, append, repair, publication synchronization, or a
306
+ checkpoint fails, recording stops for that lease without changing the agent
307
+ turn, durable session settlement, reply, or exit outcome. The headless command
308
+ attempts this warning once on stderr:
309
+
310
+ ```text
311
+ playbook run: warning: replay history for session "<session-id>" may be incomplete; recording has stopped
312
+ ```
313
+
314
+ The interactive child instead presents one Captain status with the same
315
+ `warning: replay history ...` text and writes no raw stderr diagnostic. A later
316
+ lease gets its own warning only if its replay writer independently becomes
317
+ incomplete. The public facade emits no warning; an embedding host decides how
318
+ to present the `incomplete` status it receives
319
+ ([[playbook-cli-84](https://github.com/sublang-ai/playbook/blob/main/specs/packages/playbook-cli.md#playbook-cli-84)]).
320
+
275
321
  ### Reconciling possible repository effects
276
322
 
277
323
  For each governed player call, Playbook records a Git baseline before the call
278
324
  and a durable receipt afterward. CODE and DECIDE commit arms, and REVIEW's
279
325
  Coder commit arm, accept a commit only when the receipt proves exactly one
280
- descendant commit with no residual repository change; REVIEW's Reviewer calls
281
- and DECIDE's proposal calls require the repository to remain exact. The
326
+ descendant commit with no residual repository change; REVIEW's Reviewer calls,
327
+ DECIDE's proposal calls, and every DEV Analyst planning call require the
328
+ repository to remain exact. The
282
329
  player's prose, including any `Commit:` line, is presentation rather than
283
330
  proof.
284
331
 
@@ -307,8 +354,9 @@ playbook run --session 4f2c0000-0000-4000-8000-000000009ab1 --discard-uncertain
307
354
  ```
308
355
 
309
356
  Retry reads no input and reuses the byte-exact recorded turn and its exact
310
- attempted Captain, player, and per-role model/effort selections; current config
311
- cannot retune that attempt, and retry may duplicate external effects. Discard
357
+ attempted Captain, player, and per-role model, effort, and fast-mode settings;
358
+ current config cannot retune that attempt, and retry may duplicate external
359
+ effects. Discard
312
360
  reads no input and runs no model: it restores the exact prior settled boundary,
313
361
  or deletes a never-settled fresh session, while abandoning the attempted work.
314
362
  An interrupted interactive turn uses the same uncertain record and is
@@ -4,22 +4,35 @@
4
4
  # Configuring agents
5
5
 
6
6
  Fresh launches and ordinary reopens read one config at
7
- `${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook.config.yaml`. The
7
+ `${SPEX_HOME:-$HOME/.spex}/playbook/playbook.config.yaml`. The
8
8
  first launch seeds it from the bundled starter and prints the path;
9
9
  later launches reuse it untouched.
10
10
 
11
+ On the first launching command after upgrading Playbook, it moves a config from the
12
+ former `${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook.config.yaml` path
13
+ when the canonical path is absent. The one-time move preserves bytes and
14
+ permissions and leaves no compatibility alias, so running an older Spex host
15
+ afterward could seed a second file at the former path.
16
+
17
+ The current guard rejects relocation when a legacy relative `sessions` value
18
+ or relative filesystem `playbooks.<id>.from` would resolve differently below
19
+ the new directory. It leaves the former file unchanged and names every
20
+ target-preserving absolute replacement. Apply those replacements and retry;
21
+ Playbook does not rewrite the user-authored file.
22
+
11
23
  ```sh
12
- $EDITOR "${XDG_CONFIG_HOME:-$HOME/.config}/playbook/playbook.config.yaml"
24
+ $EDITOR "${SPEX_HOME:-$HOME/.spex}/playbook/playbook.config.yaml"
13
25
  ```
14
26
 
15
27
  ## Anatomy
16
28
 
17
29
  The config is top-level (no `config:` wrapper): a `captain` agent, one flat
18
30
  `players` map of stable Captain-session agents, a `playbooks` map of enabled
19
- workflows and their explicit role bindings, and optional `layout` /
20
- `notifications` / `theme`. The Captain runs hidden control and judge calls and
21
- writes the replies you see in the Captain pane or on headless stdout. The three
22
- presentation fields apply only to interactive tmux; headless runs ignore them.
31
+ workflows and their explicit role bindings, an optional `sessions` storage
32
+ locator, and optional `layout` / `notifications` / `theme`. The Captain runs
33
+ hidden control and judge calls and writes the replies you see in the Captain
34
+ pane or on headless stdout. The three presentation fields apply only to
35
+ interactive tmux; headless runs ignore them.
23
36
 
24
37
  A **role** is local to a playbook artifact: CODE's `coder` and REVIEW's `coder`
25
38
  have the same semantic name but remain separate declarations. A **player** is
@@ -29,11 +42,12 @@ names, nesting, and ancestry never infer a binding.
29
42
 
30
43
  Each `captain` or `players.<player-id>` value is either an adapter shorthand
31
44
  (`claude`, `codex`) or a block carrying that agent's own `adapter`, `model`,
32
- `effort`, `instruction`, and `permissions`. Settings are inline per stable
33
- agent ([DR-021](https://github.com/sublang-ai/playbook/blob/main/specs/decisions/021-inline-agent-settings.md)). Dots in a
34
- player ID are literal characters, not YAML hierarchy. Other adapter IDs pass
35
- through to `tmux-play` with a warning because `playbook` cannot preflight their
36
- auth.
45
+ `effort`, `fastMode`, `instruction`, and `permissions`. Settings are inline per
46
+ stable agent
47
+ ([DR-021](https://github.com/sublang-ai/playbook/blob/main/specs/decisions/021-inline-agent-settings.md)).
48
+ Dots in a player ID are literal characters, not YAML hierarchy. Other adapter
49
+ IDs pass through to `tmux-play` with a warning because `playbook` cannot
50
+ preflight their auth.
37
51
 
38
52
  Within a `playbooks.<id>` block, `from` (the registry module), `command` (an
39
53
  optional slash-command override), and `roles` are launcher-owned; every other
@@ -41,33 +55,41 @@ key is that playbook's option slice. Every manifest role must be present
41
55
  exactly once. The launcher injects the rest — you do not write host wiring by
42
56
  hand.
43
57
 
44
- The seeded config runs the stable Coder player on Claude Opus 4.8 1m and the
45
- stable Reviewer player on GPT-5.5:
58
+ The seeded config runs the stable Coder player on GPT-5.6 Sol with fast mode
59
+ enabled and the stable Reviewer player on Claude Opus 5:
46
60
 
47
61
  ```yaml
48
62
  captain:
49
63
  adapter: claude
50
- model: claude-opus-4-8
64
+ model: claude-opus-5
51
65
  effort: high
52
66
  permissions:
53
67
  mode: auto # protected auto mode for the Claude Captain
54
68
 
55
69
  players:
56
70
  dev.coder:
71
+ adapter: codex
72
+ model: gpt-5.6-sol
73
+ effort: ultra
74
+ fastMode: true
75
+ permissions:
76
+ mode: auto
77
+ writablePaths:
78
+ - .git # allow git metadata writes under Codex auto mode
79
+
80
+ dev.reviewer:
57
81
  adapter: claude
58
- model: claude-opus-4-8[1m]
82
+ model: claude-opus-5
59
83
  effort: xhigh
60
84
  permissions:
61
- mode: auto # protected auto mode for the Claude Coder
85
+ mode: auto # protected auto mode for the Claude Reviewer
62
86
 
63
- dev.reviewer:
64
- adapter: codex
65
- model: gpt-5.5
87
+ dev.analyst:
88
+ adapter: claude
89
+ model: claude-opus-5
66
90
  effort: xhigh
67
91
  permissions:
68
- mode: auto
69
- writablePaths:
70
- - .git # allow git metadata writes under Codex auto mode
92
+ mode: auto # protected auto mode for the Claude Analyst
71
93
 
72
94
  playbooks:
73
95
  code:
@@ -86,6 +108,11 @@ playbooks:
86
108
  roles:
87
109
  coder: dev.coder
88
110
  reviewer: dev.reviewer
111
+
112
+ dev:
113
+ from: '@sublang/playbook/dev/registry'
114
+ roles:
115
+ analyst: dev.analyst
89
116
  ```
90
117
 
91
118
  The current bundled workflows accept no workflow-specific options.
@@ -104,21 +131,26 @@ roles:
104
131
  coder: dev.coder
105
132
  ```
106
133
 
107
- Use a block to override only that role invocation's model or effort:
134
+ Use a block to override only that role invocation's model, effort, or fast
135
+ mode:
108
136
 
109
137
  ```yaml
110
138
  roles:
111
139
  coder:
112
140
  player: dev.coder
113
- model: claude-opus-4-8[1m]
141
+ model: gpt-5.6-sol
114
142
  effort: false # explicitly reset to this provider's default
143
+ fastMode: false # literal disabled request, not a default sentinel
115
144
  ```
116
145
 
117
- Omitting `model` or `effort` inherits that player's top-level default. The
118
- boolean `false` is different: it selects the provider default explicitly, so a
119
- resumed conversation cannot accidentally retain an earlier selection. A role
120
- binding cannot override adapter, instruction, permissions, workspace, or tool
121
- posture; those define the stable player envelope.
146
+ Omitting any override inherits that player's top-level default. For `model`
147
+ and `effort`, boolean `false` selects the provider default explicitly, so a
148
+ resumed conversation cannot accidentally retain an earlier selection. For
149
+ `fastMode`, `false` is a literal request to disable fast mode; omitting the
150
+ top-level setting selects the provider default. A present fast-mode boolean is
151
+ accepted only for adapters Cligent reports as supporting it. A role binding
152
+ cannot override adapter, instruction, permissions, workspace, or tool posture;
153
+ those define the stable player envelope.
122
154
 
123
155
  ## Sharing, isolation, and concurrency
124
156
 
@@ -126,7 +158,9 @@ Two bindings that name the same player ID deliberately share one sequential
126
158
  provider conversation throughout the logical Captain session — across nested
127
159
  calls, returns, and later root engagements. CODE's and REVIEW's `coder` roles
128
160
  therefore share `dev.coder` in the starter, and DECIDE and its nested REVIEW
129
- share both starter players. Disposal of one playbook frame does not clear that
161
+ share both starter players. DEV's `analyst` instead binds the distinct
162
+ `dev.analyst` player, so planning context does not bleed into the shared
163
+ review conversation. Disposal of one playbook frame does not clear that
130
164
  session ledger.
131
165
 
132
166
  Two distinct player IDs stay isolated even when their agent blocks are
@@ -136,9 +170,9 @@ second top-level player and change only its binding:
136
170
  ```yaml
137
171
  players:
138
172
  review.coder:
139
- adapter: claude
140
- model: claude-opus-4-8[1m]
141
- effort: xhigh
173
+ adapter: codex
174
+ model: gpt-5.6-sol
175
+ effort: ultra
142
176
 
143
177
  playbooks:
144
178
  review:
@@ -183,8 +217,9 @@ playbook run --with fast-lineup.yaml "/code implement the approved change"
183
217
  # fast-lineup.yaml — retune the shared Coder; nothing is written back.
184
218
  players:
185
219
  dev.coder:
186
- model: claude-opus-4-8
220
+ model: gpt-5.6-sol
187
221
  effort: medium
222
+ fastMode: false
188
223
  ```
189
224
 
190
225
  Fragments merge into the agent block rather than replacing it, so
@@ -207,9 +242,37 @@ The global file is never modified, and `--with` is not forwarded to
207
242
  Overlays apply when creating a fresh session and as current-config input for a
208
243
  compatible ordinary reopen. A selected session keeps its stored catalog,
209
244
  player roster, role bindings, adapter, instruction, permissions, and working
210
- directory; only model and effort may change. The next call reapplies both
211
- complete selections. An uncertain retry accepts no tuning overlay and uses the
212
- exact attempted selections already stored with that turn.
245
+ directory; only model, effort, and fast mode may change. The next call reapplies
246
+ both complete model and effort selections and the optional effective fast-mode
247
+ boolean. An uncertain retry accepts no tuning overlay and uses the exact
248
+ attempted settings already stored with that turn.
249
+
250
+ ## Session storage
251
+
252
+ Both front ends select canonical session manifests and write replay streams in
253
+ one directory, where external hosts may keep their own sidecars too. Set the
254
+ optional top-level `sessions` key to move that shared store:
255
+
256
+ ```yaml
257
+ sessions: ./state/playbook-sessions
258
+ ```
259
+
260
+ The value must be a nonempty filesystem path. When the key is absent, the
261
+ directory is
262
+ `${XDG_STATE_HOME:-$HOME/.local/state}/playbook/sessions`. An absolute path is
263
+ used as given; `~` and `~/...` expand from the home directory, while `~user`
264
+ is rejected. Every other value, including a bare relative path such as the one
265
+ above, resolves against the primary config file's directory rather than the
266
+ invocation directory. A `sessions` value in a `--with` overlay replaces the
267
+ primary value, with later overlays winning.
268
+
269
+ Launch validates that the resolved path can serve as the mode-`0700`, real,
270
+ non-symlink session store before selecting a record or starting agent work and
271
+ fails closed when it cannot. The non-launching `playbook --list` command still
272
+ validates the locator's syntax but does not inspect that directory's filesystem
273
+ usability. The resolved locator is launch configuration only: it never enters
274
+ a persisted structural or execution projection
275
+ ([[playbook-cli-78](https://github.com/sublang-ai/playbook/blob/main/specs/packages/playbook-cli.md#playbook-cli-78)]).
213
276
 
214
277
  ## Durable shared configuration
215
278
 
@@ -223,9 +286,9 @@ public UUID. Presentation-only fields are inert headlessly.
223
286
  An ordinary reopen reads current config and opening overlays, but first
224
287
  projects them to the stored playbooks and referenced players. An unrelated new
225
288
  entry cannot enter or invalidate the session. Structural drift fails closed;
226
- compatible model or effort changes apply on the next provider call. Legacy
227
- record, shell, runtime-snapshot, and trace schemas are rejected rather than
228
- having role or player identity guessed.
289
+ compatible model, effort, or fast-mode changes apply on the next provider
290
+ call. Legacy record, shell, runtime-snapshot, and trace schemas are rejected
291
+ rather than having role or player identity guessed.
229
292
 
230
293
  ## External playbooks
231
294
 
package/docs/embedding.md CHANGED
@@ -220,6 +220,94 @@ capability functions never enter Boss-visible status text or configured
220
220
  options. Because trace observers do receive opaque resume tokens, persisted
221
221
  traces should be protected as sensitive data.
222
222
 
223
+ ## Sharing the CLI session store
224
+
225
+ An external host that needs the CLI's canonical session validation and
226
+ token-free replay format can use the narrow, semver-stable
227
+ `@sublang/playbook/session-store` facade. It shares the CLI's private store and
228
+ validators by construction but exposes no canonical manifest, snapshot,
229
+ provider credential, effect ledger, recovery operation, or turn-lifecycle
230
+ operation:
231
+
232
+ ```ts
233
+ import {
234
+ defaultSessionsDir,
235
+ openSessionStore,
236
+ } from '@sublang/playbook/session-store';
237
+
238
+ const store = openSessionStore(defaultSessionsDir());
239
+ const { sessions, skipped } = await store.list();
240
+
241
+ const sessionId = sessions[0]?.sessionId;
242
+ if (sessionId !== undefined) {
243
+ const summary = await store.read(sessionId);
244
+ console.log(summary.sessionId, summary.state, summary.cwd);
245
+
246
+ const first = await store.readStream(sessionId);
247
+ const next = await store.readStream(sessionId, {
248
+ afterSeq: first.lastReadableSeq,
249
+ });
250
+ console.log(next.entries, skipped);
251
+ }
252
+ ```
253
+
254
+ `list()` reports valid summaries and separately reports skipped canonical
255
+ manifests with their validation or cutover reason. A summary has exactly
256
+ `schemaVersion`, `sessionId`, `state`, `cwd`, and `updatedAt`. A lease-free
257
+ `readStream()` returns complete envelopes and `lastReadableSeq` only: it makes
258
+ no claim that another process has durably checkpointed the observed bytes or
259
+ that its live writer remains complete. An absent stream reads as empty, and
260
+ acquiring its lease does not require a manifest; only `read()` requires a
261
+ canonical session summary. Pass an absolute path to `openSessionStore()` when
262
+ using a directory other than the environment-derived default.
263
+
264
+ Writing requires the one exclusive session lease. The writer assigns envelope
265
+ version and sequence, serializes overlapping appends in invocation order, and
266
+ strips provider resume credentials from every accepted record:
267
+
268
+ ```ts
269
+ const lease = await store.acquire(
270
+ '4f2c0000-0000-4000-8000-000000009ab1',
271
+ );
272
+ try {
273
+ await lease.append({ type: 'host_notice', message: 'attached' });
274
+ const status = lease.streamStatus();
275
+ console.log(status);
276
+ if (status.lastReadableSeq !== null) {
277
+ const replay = await lease.readStream();
278
+ console.log(replay.lastReadableSeq, replay.lastDurableSeq);
279
+ }
280
+ } finally {
281
+ const finalStatus = await lease.release();
282
+ console.log(finalStatus);
283
+ }
284
+ ```
285
+
286
+ `streamStatus()` synchronously returns the current live status. If initialization
287
+ could not establish a trustworthy whole-stream boundary, it returns
288
+ `{ lastReadableSeq: null, lastDurableSeq: null, incomplete: true }`, and
289
+ `lease.readStream()` rejects rather than return partial history.
290
+ An `append()` suppressed before release by either unavailable initialization or
291
+ a numeric incomplete latch resolves `undefined` without recording the supplied
292
+ record, so fulfillment alone does not prove persistence.
293
+
294
+ Always release a successfully acquired lease. `release()` closes append
295
+ admission, drains earlier appends, attempts the final checkpoint, retires the
296
+ lease, and returns the final `lastReadableSeq`, `lastDurableSeq`, and
297
+ `incomplete` status. A replay initialization failure reports unavailable null
298
+ boundaries; a later sanitization, repair, or persistence failure latches
299
+ numeric `incomplete` status. Either state suppresses later replay work on that
300
+ lease and remains isolated from canonical session lifecycle work. The facade
301
+ writes no warning to stdout or stderr; the embedding host owns any presentation
302
+ of that status.
303
+
304
+ For control flow, a missing canonical manifest from `read()` uses
305
+ `Error.code === 'PLAYBOOK_SESSION_NOT_FOUND'`, and a competing live or foreign
306
+ lease uses `Error.code === 'PLAYBOOK_SESSION_LEASE_ACTIVE'`. Do not match error
307
+ messages or assume those codes for malformed input, unsafe storage, an
308
+ indeterminate owner probe, or another storage failure
309
+ ([[playbook-cli-73](https://github.com/sublang-ai/playbook/blob/main/specs/packages/playbook-cli.md#playbook-cli-73)]).
310
+
223
311
  See
224
312
  [`code.playbook.test.ts`](https://github.com/sublang-ai/playbook/blob/main/reference/sdlc/code.playbook/code.playbook.test.ts)
225
313
  for the full range of port shapes (classifier, judge, abort, interrupt,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@sublang/playbook",
3
- "version": "10.0.0",
3
+ "version": "12.0.0",
4
4
  "type": "module",
5
- "description": "Composable XState v5 playbook runtime with compiled Captain, CODE, REVIEW, and DECIDE workflows driven by GEARS specs.",
5
+ "description": "Composable XState v5 playbook runtime with compiled Captain, CODE, REVIEW, DECIDE, and DEV workflows driven by GEARS specs.",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://github.com/sublang-ai/playbook#readme",
8
8
  "repository": {
@@ -45,6 +45,7 @@
45
45
  "reference/sdlc/code.md",
46
46
  "reference/sdlc/review.md",
47
47
  "reference/sdlc/decide.md",
48
+ "reference/sdlc/dev.md",
48
49
  "reference/sdlc/captain.playbook/captain.gears.md",
49
50
  "reference/sdlc/captain.playbook/captain.fsm.ts",
50
51
  "reference/sdlc/captain.playbook/captain.fsm.js",
@@ -67,12 +68,15 @@
67
68
  "reference/sdlc/code.playbook/playbook-captain.ts",
68
69
  "reference/sdlc/code.playbook/playbook-captain.js",
69
70
  "reference/sdlc/code.playbook/playbook-captain.d.ts",
71
+ "reference/sdlc/code.playbook/session-store.js",
72
+ "reference/sdlc/code.playbook/session-store.d.ts",
70
73
  "reference/sdlc/code.playbook/code.gears.md",
71
74
  "reference/sdlc/code.playbook/playbook.config.template.yaml",
72
75
  "reference/sdlc/code.playbook/bin/playbook.js",
73
76
  "reference/sdlc/code.playbook/bin/launch-config.js",
74
77
  "reference/sdlc/code.playbook/bin/run.js",
75
78
  "reference/sdlc/code.playbook/bin/interactive-session.js",
79
+ "reference/sdlc/code.playbook/bin/replay-observer.js",
76
80
  "reference/sdlc/code.playbook/bin/session-store.js",
77
81
  "reference/sdlc/code.playbook/bin/provision.js",
78
82
  "reference/sdlc/code.playbook/bin/adapter-sdk.js",
@@ -97,6 +101,16 @@
97
101
  "reference/sdlc/decide.playbook/decide.registry.ts",
98
102
  "reference/sdlc/decide.playbook/decide.registry.js",
99
103
  "reference/sdlc/decide.playbook/decide.registry.d.ts",
104
+ "reference/sdlc/dev.playbook/dev.gears.md",
105
+ "reference/sdlc/dev.playbook/dev.fsm.ts",
106
+ "reference/sdlc/dev.playbook/dev.fsm.js",
107
+ "reference/sdlc/dev.playbook/dev.fsm.d.ts",
108
+ "reference/sdlc/dev.playbook/dev.playbook.ts",
109
+ "reference/sdlc/dev.playbook/dev.playbook.js",
110
+ "reference/sdlc/dev.playbook/dev.playbook.d.ts",
111
+ "reference/sdlc/dev.playbook/dev.registry.ts",
112
+ "reference/sdlc/dev.playbook/dev.registry.js",
113
+ "reference/sdlc/dev.playbook/dev.registry.d.ts",
100
114
  "docs"
101
115
  ],
102
116
  "bin": {
@@ -127,6 +141,10 @@
127
141
  "types": "./reference/sdlc/code.playbook/playbook-captain.d.ts",
128
142
  "default": "./reference/sdlc/code.playbook/playbook-captain.js"
129
143
  },
144
+ "./session-store": {
145
+ "types": "./reference/sdlc/code.playbook/session-store.d.ts",
146
+ "default": "./reference/sdlc/code.playbook/session-store.js"
147
+ },
130
148
  "./slc/*": "./slc/*",
131
149
  "./review/playbook": {
132
150
  "types": "./reference/sdlc/review.playbook/review.playbook.d.ts",
@@ -143,6 +161,14 @@
143
161
  "./decide/registry": {
144
162
  "types": "./reference/sdlc/decide.playbook/decide.registry.d.ts",
145
163
  "default": "./reference/sdlc/decide.playbook/decide.registry.js"
164
+ },
165
+ "./dev/playbook": {
166
+ "types": "./reference/sdlc/dev.playbook/dev.playbook.d.ts",
167
+ "default": "./reference/sdlc/dev.playbook/dev.playbook.js"
168
+ },
169
+ "./dev/registry": {
170
+ "types": "./reference/sdlc/dev.playbook/dev.registry.d.ts",
171
+ "default": "./reference/sdlc/dev.playbook/dev.registry.js"
146
172
  }
147
173
  },
148
174
  "scripts": {
@@ -158,14 +184,14 @@
158
184
  "provenance": true
159
185
  },
160
186
  "dependencies": {
161
- "@sublang/cligent": "^0.23.0",
187
+ "@sublang/cligent": "^0.24.0",
162
188
  "@sublang/spex": "^3.0.0",
163
189
  "p-queue": "^9.3.1",
164
190
  "xstate": "^5.19.4",
165
191
  "yaml": "^2.9.0"
166
192
  },
167
193
  "devDependencies": {
168
- "@anthropic-ai/claude-agent-sdk": "^0.3.221",
194
+ "@anthropic-ai/claude-agent-sdk": "^0.3.223",
169
195
  "@openai/codex-sdk": "^0.146.0",
170
196
  "@types/node": "^22.0.0",
171
197
  "typescript": "^5.8.0",