@sublang/playbook 7.0.0 → 8.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 (61) hide show
  1. package/README.md +17 -4
  2. package/docs/cli.md +74 -29
  3. package/docs/configuration.md +209 -112
  4. package/docs/embedding.md +71 -25
  5. package/package.json +4 -3
  6. package/reference/sdlc/captain.playbook/captain.playbook.js +3 -3
  7. package/reference/sdlc/captain.playbook/captain.playbook.ts +3 -3
  8. package/reference/sdlc/code.md +1 -1
  9. package/reference/sdlc/code.playbook/bin/interactive-session.js +816 -0
  10. package/reference/sdlc/code.playbook/bin/launch-config.js +1078 -116
  11. package/reference/sdlc/code.playbook/bin/playbook.js +489 -34
  12. package/reference/sdlc/code.playbook/bin/run.js +283 -298
  13. package/reference/sdlc/code.playbook/bin/session-store.js +818 -26
  14. package/reference/sdlc/code.playbook/code.fsm.d.ts +5 -5
  15. package/reference/sdlc/code.playbook/code.fsm.introspect.js +2 -2
  16. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +2 -2
  17. package/reference/sdlc/code.playbook/code.fsm.js +7 -11
  18. package/reference/sdlc/code.playbook/code.fsm.ts +9 -17
  19. package/reference/sdlc/code.playbook/code.gears.md +1 -1
  20. package/reference/sdlc/code.playbook/code.playbook.d.ts +2 -1
  21. package/reference/sdlc/code.playbook/code.playbook.js +12 -13
  22. package/reference/sdlc/code.playbook/code.playbook.ts +22 -15
  23. package/reference/sdlc/code.playbook/code.registry.d.ts +5 -13
  24. package/reference/sdlc/code.playbook/code.registry.js +3 -10
  25. package/reference/sdlc/code.playbook/code.registry.ts +7 -32
  26. package/reference/sdlc/code.playbook/playbook-captain.d.ts +39 -14
  27. package/reference/sdlc/code.playbook/playbook-captain.js +970 -289
  28. package/reference/sdlc/code.playbook/playbook-captain.ts +1403 -396
  29. package/reference/sdlc/code.playbook/playbook.config.template.yaml +41 -49
  30. package/reference/sdlc/decide.md +4 -4
  31. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +9 -9
  32. package/reference/sdlc/decide.playbook/decide.fsm.js +21 -14
  33. package/reference/sdlc/decide.playbook/decide.fsm.ts +27 -23
  34. package/reference/sdlc/decide.playbook/decide.gears.md +3 -5
  35. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +9 -13
  36. package/reference/sdlc/decide.playbook/decide.playbook.js +171 -134
  37. package/reference/sdlc/decide.playbook/decide.playbook.ts +238 -162
  38. package/reference/sdlc/decide.playbook/decide.registry.d.ts +5 -13
  39. package/reference/sdlc/decide.playbook/decide.registry.js +3 -9
  40. package/reference/sdlc/decide.playbook/decide.registry.ts +7 -31
  41. package/reference/sdlc/review.md +4 -5
  42. package/reference/sdlc/review.playbook/review.fsm.d.ts +9 -11
  43. package/reference/sdlc/review.playbook/review.fsm.js +30 -24
  44. package/reference/sdlc/review.playbook/review.fsm.ts +39 -35
  45. package/reference/sdlc/review.playbook/review.gears.md +6 -5
  46. package/reference/sdlc/review.playbook/review.playbook.d.ts +2 -1
  47. package/reference/sdlc/review.playbook/review.playbook.js +16 -21
  48. package/reference/sdlc/review.playbook/review.playbook.ts +26 -26
  49. package/reference/sdlc/review.playbook/review.registry.d.ts +5 -13
  50. package/reference/sdlc/review.playbook/review.registry.js +3 -16
  51. package/reference/sdlc/review.playbook/review.registry.ts +7 -38
  52. package/slc/gears2fsm.md +27 -23
  53. package/slc/link.md +113 -93
  54. package/slc/text2gears.md +19 -18
  55. package/src/runtime.d.ts +20 -16
  56. package/src/runtime.ts +19 -23
  57. package/src/xstate-playbook-runtime.d.ts +21 -17
  58. package/src/xstate-playbook-runtime.js +241 -149
  59. package/src/xstate-playbook-runtime.ts +331 -178
  60. package/src/xstate-runtime.js +63 -24
  61. package/src/xstate-runtime.ts +96 -28
package/docs/embedding.md CHANGED
@@ -16,8 +16,8 @@ your own host.
16
16
  The port and runtime contracts live in the type-only module
17
17
  [`@sublang/playbook/runtime`](../src/runtime.ts) — a public,
18
18
  semver-stable surface (`PlayerResult`, `PlaybookPorts`,
19
- `PlaybookRuntime`, `PlaybookSession`, `PlayerCallOptions`,
20
- `PlayerSessionStore`, `CaptainCallOptions`, `CaptainResult`,
19
+ `PlaybookRuntime`, `PlaybookSession`, `PlaybookRoleBinding`,
20
+ `PlayerCallOptions`, `PlayerSessionStore`, `CaptainCallOptions`, `CaptainResult`,
21
21
  `PlaybookTraceEvent`, and `PlaybookRuntimeFactory`) that imports no CODE
22
22
  or FSM types, so a host satisfies it once and inherits every playbook.
23
23
  The generated CODE, REVIEW, and DECIDE modules re-export their shared
@@ -37,6 +37,9 @@ import type {
37
37
  CaptainCallOptions,
38
38
  CaptainResult,
39
39
  PlaybookPorts,
40
+ PlaybookRoleBinding,
41
+ PlayerResult,
42
+ PlayerSessionStore,
40
43
  } from '@sublang/playbook/runtime';
41
44
  import { randomUUID } from 'node:crypto';
42
45
  import PQueue from 'p-queue';
@@ -53,6 +56,33 @@ declare const captainAdapter: {
53
56
  ): Promise<CaptainResult>;
54
57
  };
55
58
 
59
+ declare const playerAdapter: {
60
+ run(
61
+ playerId: string,
62
+ prompt: string,
63
+ options: { signal: AbortSignal; resume: string | false },
64
+ ): Promise<PlayerResult>;
65
+ };
66
+
67
+ // Roles are local workflow identities. Players are stable provider
68
+ // conversations owned by the logical Captain session. `promptIdentity` is
69
+ // the current model name, or the player's adapter when provider-default is
70
+ // selected; rebuild it from current compatible tuning on restore.
71
+ const roleBindings = {
72
+ coder: {
73
+ playerId: 'team.coder',
74
+ promptIdentity: 'claude-opus-4-8[1m]',
75
+ },
76
+ reviewer: {
77
+ playerId: 'team.reviewer',
78
+ promptIdentity: 'gpt-5.5',
79
+ },
80
+ } satisfies Readonly<Record<string, PlaybookRoleBinding>>;
81
+
82
+ // Supply a frame-local role view over your session-wide player ledger.
83
+ // Equal player IDs must select/update the same token; distinct IDs must not.
84
+ declare const playerSessions: PlayerSessionStore;
85
+
56
86
  // Construct one host-wide lane and reuse it for every runtime. Passing each
57
87
  // call's signal to both the lane and adapter cancels queued and active work.
58
88
  const captainLane = new PQueue({ concurrency: 1 });
@@ -69,10 +99,16 @@ async function runCaptain(
69
99
  }
70
100
 
71
101
  const ports: PlaybookPorts = {
72
- callPlayer: async (playerId, prompt, signal, { resume }) => {
102
+ callPlayer: async (roleId, prompt, signal, { resume }) => {
103
+ const binding = roleBindings[roleId as keyof typeof roleBindings];
104
+ if (binding === undefined) throw new Error(`Unknown role: ${roleId}`);
73
105
  // `resume === false` starts fresh; a string selects that player's
74
- // prior backend conversation. Return the adapter's next token.
75
- return { status: 'ok', finalText: 'done', resumeToken: 'next-token' };
106
+ // prior backend conversation. Return the adapter's next token; the
107
+ // runtime updates `playerSessions` only after validating this result.
108
+ return await playerAdapter.run(binding.playerId, prompt, {
109
+ signal,
110
+ resume,
111
+ });
76
112
  },
77
113
  callCaptain: async (prompt, signal, options) => {
78
114
  // Forward every option exactly: omission preserves configured tools, while
@@ -103,10 +139,7 @@ const ports: PlaybookPorts = {
103
139
  },
104
140
  };
105
141
 
106
- const runtime = createPlaybookRuntime({
107
- coderLlm: 'claude-opus-4-8[1m]',
108
- reviewerLlm: 'gpt-5.5',
109
- });
142
+ const runtime = createPlaybookRuntime({});
110
143
 
111
144
  const playbookSessionId = randomUUID();
112
145
  await runtime.init({
@@ -114,6 +147,8 @@ await runtime.init({
114
147
  playbookId: 'review',
115
148
  rootSessionId: playbookSessionId,
116
149
  depth: 0,
150
+ roleBindings,
151
+ playerSessions,
117
152
  ports,
118
153
  });
119
154
  await runtime.handleBossInput({
@@ -125,23 +160,34 @@ await runtime.dispose();
125
160
 
126
161
  ## Sessions and traces
127
162
 
128
- Every init-to-dispose lifecycle is one playbook session. Its
163
+ Every init-to-dispose lifecycle is one playbook session. Schema-3
129
164
  `playbook.trace` telemetry carries that immutable ID plus a contiguous
130
- sequence across exact Boss input, judge/player calls, FSM transitions,
131
- visible Captain work, nested playbook calls, status, settlement, and
132
- disposal. Without `PlaybookSession.playerSessions`, a standalone runtime
133
- starts each player fresh and privately retains the latest opaque
134
- `resumeToken` its adapter returned.
135
-
136
- A composing host can instead supply a frame-local `PlayerSessionStore`
137
- view over one root-owned continuation map. The host maps each local role
138
- to its effective binding, so a nested exact same-name role selects and
139
- updates the ancestor conversation while an unmatched child role gets
140
- its own binding. Child return or disposal does not clear the root's
141
- token, and a new root engagement starts fresh. Trace data and tokens
142
- never enter Boss-visible status text. Because trace observers do receive
143
- opaque resume tokens, persisted traces should be protected as sensitive
144
- data.
165
+ sequence across exact Boss input, judge/player calls, FSM transitions, visible
166
+ Captain work, nested playbook calls, status, settlement, and disposal. A
167
+ shell-hosted player boundary keeps both identities: `roleId` says which local
168
+ workflow job made the call, while `playerId` says which stable session
169
+ conversation owned it. A standalone runtime retains the role without
170
+ inventing host player identity.
171
+
172
+ Without `PlaybookSession.playerSessions`, a standalone runtime starts each
173
+ local role fresh and privately retains the latest opaque `resumeToken` its
174
+ adapter returned. A composing host instead supplies a frame-local
175
+ `PlayerSessionStore` view over one Captain-session ledger and explicit
176
+ `roleBindings`. The store's methods receive local role IDs; the view resolves
177
+ them to the configured stable player IDs. Equal IDs share one token and
178
+ sequential call lane across every frame that names them, while distinct IDs
179
+ remain isolated. Child return, frame disposal, and a later root engagement do
180
+ not clear the session ledger.
181
+
182
+ Runtime snapshots are schema 3. Their `roleResumeTokens` projection remains
183
+ role-local, while the composing shell's own schema-3 snapshot persists the
184
+ stable player ledger and every frame's exact role bindings. Do not restore
185
+ schema 1 or 2 by guessing identity. On a compatible restore, rebuild
186
+ `promptIdentity` from the current model selection (or adapter for an explicit
187
+ provider-default selection) so the next prompt and trace describe the current
188
+ invocation rather than stale machine state. Trace data and tokens never enter
189
+ Boss-visible status text. Because trace observers do receive opaque resume
190
+ tokens, persisted traces should be protected as sensitive data.
145
191
 
146
192
  See
147
193
  [`code.playbook.test.ts`](../reference/sdlc/code.playbook/code.playbook.test.ts)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sublang/playbook",
3
- "version": "7.0.0",
3
+ "version": "8.0.0",
4
4
  "type": "module",
5
5
  "description": "Composable XState v5 playbook runtime with compiled Captain, CODE, REVIEW, and DECIDE workflows driven by GEARS specs.",
6
6
  "license": "Apache-2.0",
@@ -69,6 +69,7 @@
69
69
  "reference/sdlc/code.playbook/bin/playbook.js",
70
70
  "reference/sdlc/code.playbook/bin/launch-config.js",
71
71
  "reference/sdlc/code.playbook/bin/run.js",
72
+ "reference/sdlc/code.playbook/bin/interactive-session.js",
72
73
  "reference/sdlc/code.playbook/bin/session-store.js",
73
74
  "reference/sdlc/code.playbook/bin/provision.js",
74
75
  "reference/sdlc/code.playbook/bin/adapter-sdk.js",
@@ -142,7 +143,7 @@
142
143
  },
143
144
  "scripts": {
144
145
  "build": "tsc",
145
- "test": "spex lint && vitest run",
146
+ "test": "spex lint && vitest run --exclude src/cligent-release-capabilities.test.ts && vitest run src/cligent-release-capabilities.test.ts",
146
147
  "test:acceptance": "vitest run --config vitest.acceptance.config.ts",
147
148
  "smoke:release": "node scripts/release-smoke.mjs",
148
149
  "check:links": "node scripts/check-links.mjs",
@@ -153,7 +154,7 @@
153
154
  "provenance": true
154
155
  },
155
156
  "dependencies": {
156
- "@sublang/cligent": "^0.19.0",
157
+ "@sublang/cligent": "^0.22.0",
157
158
  "@sublang/spex": "^2.1.1",
158
159
  "p-queue": "^9.3.1",
159
160
  "xstate": "^5.19.4",
@@ -31,7 +31,7 @@
31
31
  // deterministic entry mapping, the controller captain-call
32
32
  // strategy with its single corrective re-ask (CAPPLAY-18),
33
33
  // controller-port submission, and status formatting.
34
- // Compat: spec.compat = { artifactSchema: 1, runtimeAbi: 1 }
34
+ // Compat: spec.compat = { artifactSchema: 2, runtimeAbi: 1 }
35
35
  // (DR-022; checked at construction by the loading engine).
36
36
  import { createXStatePlaybookRuntime, defaultComposeCaptainPrompt, normalizeError, normalizeErrorCompact, parseJudgeJson, snapshotJsonValue, RUNTIME_ABI, } from '../../../src/xstate-runtime.js';
37
37
  import { captainMachine, } from './captain.fsm.js';
@@ -516,11 +516,11 @@ export const _internal = {
516
516
  // describe/apply control surface — lives in @sublang/playbook/xstate-runtime.
517
517
  const runtimeSpec = {
518
518
  label: 'CAPTAIN',
519
- compat: { artifactSchema: 1, runtimeAbi: RUNTIME_ABI },
519
+ compat: { artifactSchema: 2, runtimeAbi: RUNTIME_ABI },
520
520
  snapshotOptions: snapshotCaptainOptions,
521
521
  machineInput: (options) => ({ enabledPlaybooks: options.enabledPlaybooks }),
522
522
  classifyBossText: (text, ports, signal, snapshotOrState, boundary, options) => classifyControllerTurn(text, ports, signal, snapshotOrState, boundary, options),
523
- playerStates: {},
523
+ roleStates: {},
524
524
  classificationStatus: () => undefined,
525
525
  captainStrategy: controllerCaptainStrategy,
526
526
  // CAPPLAY-10 / PBRT-52: the Captain's own ControlView context projection —
@@ -31,7 +31,7 @@
31
31
  // deterministic entry mapping, the controller captain-call
32
32
  // strategy with its single corrective re-ask (CAPPLAY-18),
33
33
  // controller-port submission, and status formatting.
34
- // Compat: spec.compat = { artifactSchema: 1, runtimeAbi: 1 }
34
+ // Compat: spec.compat = { artifactSchema: 2, runtimeAbi: 1 }
35
35
  // (DR-022; checked at construction by the loading engine).
36
36
 
37
37
  import {
@@ -798,7 +798,7 @@ export const _internal = {
798
798
  // describe/apply control surface — lives in @sublang/playbook/xstate-runtime.
799
799
  const runtimeSpec: XStatePlaybookRuntimeSpec<ValidatedCaptainOptions> = {
800
800
  label: 'CAPTAIN',
801
- compat: { artifactSchema: 1, runtimeAbi: RUNTIME_ABI },
801
+ compat: { artifactSchema: 2, runtimeAbi: RUNTIME_ABI },
802
802
  snapshotOptions: snapshotCaptainOptions,
803
803
  machineInput: (options) => ({ enabledPlaybooks: options.enabledPlaybooks }),
804
804
  classifyBossText: (text, ports, signal, snapshotOrState, boundary, options) =>
@@ -810,7 +810,7 @@ const runtimeSpec: XStatePlaybookRuntimeSpec<ValidatedCaptainOptions> = {
810
810
  boundary,
811
811
  options,
812
812
  ) as Promise<import('xstate').EventObject | undefined>,
813
- playerStates: {},
813
+ roleStates: {},
814
814
  classificationStatus: () => undefined,
815
815
  captainStrategy: controllerCaptainStrategy,
816
816
  // CAPPLAY-10 / PBRT-52: the Captain's own ControlView context projection —
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Code
6
6
 
7
- Players:
7
+ Roles:
8
8
 
9
9
  - Coder
10
10