@sublang/playbook 4.0.0 → 6.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 +69 -122
- package/docs/assets/playbook-venn.svg +13 -0
- package/docs/cli.md +43 -26
- package/docs/configuration.md +63 -18
- package/docs/embedding.md +24 -16
- package/package.json +43 -22
- package/reference/sdlc/captain.md +70 -83
- package/reference/sdlc/captain.playbook/captain.fsm.d.ts +127 -142
- package/reference/sdlc/captain.playbook/captain.fsm.js +349 -470
- package/reference/sdlc/captain.playbook/captain.fsm.ts +535 -598
- package/reference/sdlc/captain.playbook/captain.gears.md +37 -41
- package/reference/sdlc/captain.playbook/captain.playbook.d.ts +90 -15
- package/reference/sdlc/captain.playbook/captain.playbook.js +466 -976
- package/reference/sdlc/captain.playbook/captain.playbook.ts +698 -1001
- package/reference/sdlc/code.md +55 -97
- package/reference/sdlc/code.playbook/code.fsm.d.ts +229 -94
- package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +26 -44
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +61 -66
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +100 -149
- package/reference/sdlc/code.playbook/code.fsm.js +587 -1347
- package/reference/sdlc/code.playbook/code.fsm.ts +809 -1650
- package/reference/sdlc/code.playbook/code.gears.md +51 -263
- package/reference/sdlc/code.playbook/code.playbook.d.ts +8 -47
- package/reference/sdlc/code.playbook/code.playbook.js +69 -639
- package/reference/sdlc/code.playbook/code.playbook.ts +90 -850
- package/reference/sdlc/code.playbook/code.registry.d.ts +9 -25
- package/reference/sdlc/code.playbook/code.registry.js +20 -78
- package/reference/sdlc/code.playbook/code.registry.ts +58 -122
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +2 -0
- package/reference/sdlc/code.playbook/playbook-captain.js +1877 -251
- package/reference/sdlc/code.playbook/playbook-captain.ts +2385 -352
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +33 -22
- package/reference/sdlc/decide.md +54 -0
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +261 -0
- package/reference/sdlc/decide.playbook/decide.fsm.js +894 -0
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1152 -0
- package/reference/sdlc/decide.playbook/decide.gears.md +88 -0
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +67 -0
- package/reference/sdlc/{discuss.playbook/discuss.playbook.js → decide.playbook/decide.playbook.js} +511 -370
- package/reference/sdlc/{discuss.playbook/discuss.playbook.ts → decide.playbook/decide.playbook.ts} +616 -451
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +41 -0
- package/reference/sdlc/decide.playbook/decide.registry.js +60 -0
- package/reference/sdlc/decide.playbook/decide.registry.ts +125 -0
- package/reference/sdlc/review.md +81 -0
- package/reference/sdlc/review.playbook/review.fsm.d.ts +183 -0
- package/reference/sdlc/review.playbook/review.fsm.js +524 -0
- package/reference/sdlc/review.playbook/review.fsm.ts +652 -0
- package/reference/sdlc/review.playbook/review.gears.md +112 -0
- package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -0
- package/reference/sdlc/review.playbook/review.playbook.js +112 -0
- package/reference/sdlc/review.playbook/review.playbook.ts +201 -0
- package/reference/sdlc/review.playbook/review.registry.d.ts +43 -0
- package/reference/sdlc/review.playbook/review.registry.js +73 -0
- package/reference/sdlc/review.playbook/review.registry.ts +138 -0
- package/slc/gears2fsm.md +67 -6
- package/slc/link.md +339 -25
- package/slc/text2gears.md +22 -2
- package/src/runtime.d.ts +36 -1
- package/src/runtime.ts +59 -0
- package/src/xstate-playbook-runtime.d.ts +96 -7
- package/src/xstate-playbook-runtime.js +1018 -49
- package/src/xstate-playbook-runtime.ts +1283 -59
- package/src/xstate-runtime.js +25 -0
- package/src/xstate-runtime.ts +51 -0
- package/reference/sdlc/discuss.md +0 -93
- package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +0 -396
- package/reference/sdlc/discuss.playbook/discuss.fsm.js +0 -2067
- package/reference/sdlc/discuss.playbook/discuss.fsm.ts +0 -2465
- package/reference/sdlc/discuss.playbook/discuss.gears.md +0 -258
- package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +0 -113
- package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +0 -58
- package/reference/sdlc/discuss.playbook/discuss.registry.js +0 -97
- package/reference/sdlc/discuss.playbook/discuss.registry.ts +0 -153
package/slc/gears2fsm.md
CHANGED
|
@@ -167,6 +167,7 @@ The default never applies to an item carrying a `Results:` label, and it does
|
|
|
167
167
|
not license inferring any richer contract from prose.
|
|
168
168
|
The item's blockquote alone becomes `invoke.input.prompt`; the `Results:`
|
|
169
169
|
label and bullets shall never enter that prompt.
|
|
170
|
+
A `>` that remains at the start of a prompt line after removing the outer GEARS blockquote marker is literal quoted-context content and shall remain in `invoke.input.prompt` unchanged.
|
|
170
171
|
Each result description shall name every additional output field its accepting
|
|
171
172
|
guard requires, using the exact case-sensitive property names. For example, a
|
|
172
173
|
delegation or continuing-call description whose guard reads the planned child
|
|
@@ -193,6 +194,49 @@ Both direct-Captain states additionally receive the universal
|
|
|
193
194
|
`needsBossReply` result. Their guards and actions shall use those exact
|
|
194
195
|
case-sensitive names so the compiled adjudication contract remains stable.
|
|
195
196
|
|
|
197
|
+
For a controller playbook — one whose Source declares DR-029's session-scoped
|
|
198
|
+
controller policy: a session Captain that runs for the whole host session,
|
|
199
|
+
receives every Boss turn, and operates the working playbooks from outside the
|
|
200
|
+
engagement stack (DR-029) — the compiler shall apply the additive
|
|
201
|
+
controller decision-state class below. The class joins the stable compiler
|
|
202
|
+
contract beside the decide-call-observe vocabulary above; that vocabulary and
|
|
203
|
+
the universal `needsBossReply` rule stay untouched for the artifacts that
|
|
204
|
+
consume them.
|
|
205
|
+
The controller machine shall be a session loop, not a finite errand: a
|
|
206
|
+
quiescent conversational hub (tag `playbook.parked`) receives each Boss turn;
|
|
207
|
+
the controller decision state decides it over the closed action set; a
|
|
208
|
+
`respond` selection settles its turn in the decision call itself, its
|
|
209
|
+
validated `text` being the turn's captain speech; an acting selection's host
|
|
210
|
+
settlement becomes the outcome report grounding one closing-reply call; and
|
|
211
|
+
the machine returns to the hub for the next turn. Returning to the hub after
|
|
212
|
+
a settled turn completes the session loop's turn; it is not the idle-hub
|
|
213
|
+
routing that [Transitions](#transitions) reserves for recovery.
|
|
214
|
+
Because the hub receives every Boss turn, a controller state carries no
|
|
215
|
+
Boss-reply suspension: the compiler shall not add `needsBossReply` to a
|
|
216
|
+
controller machine's `invoke.input.result` maps — a clarifying question to
|
|
217
|
+
Boss is a `respond` selection.
|
|
218
|
+
The machine shall declare no terminal result output and shall keep exactly
|
|
219
|
+
one reachable `type: 'final'` shutdown state entered only by the host's
|
|
220
|
+
teardown event. The completion rule of
|
|
221
|
+
[Errors and termination](#errors-and-termination) applies unamended: its
|
|
222
|
+
output clause binds only where Source declares a terminal result, which a
|
|
223
|
+
controller Source does not.
|
|
224
|
+
The controller decision state's direct-Captain result contract discriminates
|
|
225
|
+
the closed action set of DR-029. Its guard discriminants are a stable
|
|
226
|
+
compiler contract, not names the compiler may invent — `respond`, `start`,
|
|
227
|
+
`switch`, `dismiss`, `deliver`, and `runtime` — with each guard's required
|
|
228
|
+
payload fields:
|
|
229
|
+
|
|
230
|
+
- `respond` requires `text`;
|
|
231
|
+
- `start` and `switch` each require `playbookId` and `input`;
|
|
232
|
+
- `runtime` requires `actionId`;
|
|
233
|
+
- `dismiss` and `deliver` require none — a `deliver` result in particular
|
|
234
|
+
carries no text payload: the host is authoritative for the delivered text,
|
|
235
|
+
so the contract declares no field for it.
|
|
236
|
+
|
|
237
|
+
The decision state's guards and actions shall use those exact case-sensitive
|
|
238
|
+
names so the compiled controller contract remains stable.
|
|
239
|
+
|
|
196
240
|
## States
|
|
197
241
|
|
|
198
242
|
Each state shall declare:
|
|
@@ -200,13 +244,15 @@ Each state shall declare:
|
|
|
200
244
|
- a stable `id` (for `#id` targeting and Boss interrupts);
|
|
201
245
|
- an intuitive state key (the property name under `states: { ... }`);
|
|
202
246
|
- a one-line `description` (for inspector tools and documentation);
|
|
203
|
-
- JSON-safe `meta: { playbook: { stateId, description } }` repeating
|
|
204
|
-
stable id and description so linked runtimes can discover active public
|
|
205
|
-
identities through `snapshot.getMeta()` without private XState nodes
|
|
247
|
+
- JSON-safe `meta: { playbook: { stateId, description, player? } }` repeating
|
|
248
|
+
its stable id and description so linked runtimes can discover active public
|
|
249
|
+
identities through `snapshot.getMeta()` without private XState nodes. A
|
|
250
|
+
delegated-player state shall also carry the exact source player in
|
|
251
|
+
`meta.playbook.player`; every other state shall omit `player`;
|
|
206
252
|
- if it invokes the direct `captain` actor: `invoke.input` carrying
|
|
207
253
|
`sourceItem`, `prompt`, and `result` (per [Setup](#setup));
|
|
208
254
|
- if it invokes the delegated `player` actor: `invoke.input` additionally
|
|
209
|
-
carrying `player`;
|
|
255
|
+
carrying the same source-derived `player` as `meta.playbook.player`;
|
|
210
256
|
- if it invokes the `script` actor: `invoke.input` carrying `stateId`,
|
|
211
257
|
`sourceItem`, `command`, and `result` (per [Setup](#setup)) — no `prompt`
|
|
212
258
|
and no `player`.
|
|
@@ -555,6 +601,11 @@ context precondition required to enter that target safely. It shall not jump
|
|
|
555
601
|
into a working or reassessment state with missing intent, prior result, plan,
|
|
556
602
|
or other required context and shall not invent defaults merely to make an
|
|
557
603
|
interrupt target executable.
|
|
604
|
+
Control-action discovery probes these guards with optional textual fields
|
|
605
|
+
omitted. Before applying a string operation such as `trim()`, a generated
|
|
606
|
+
guard shall narrow the field to a string; a missing required textual field
|
|
607
|
+
shall make the guard return false, never throw, so the control view remains
|
|
608
|
+
total and omits an action whose payload the runtime cannot source.
|
|
558
609
|
XState automatically stops the current state's invoked actor on transition [[2]].
|
|
559
610
|
Where the default Captain's routing state accepts a fresh intent while another
|
|
560
611
|
state or Boss-reply wait is active, its `BOSS_INTERRUPT` event shall carry a
|
|
@@ -588,11 +639,21 @@ cannot supply alone, the machine shall suspend that task in a quiescent wait
|
|
|
588
639
|
state and resume the same task with the Q+A in the next prompt.
|
|
589
640
|
This is a third Boss surface alongside `BOSS_INTERRUPT` and Boss entry events.
|
|
590
641
|
|
|
591
|
-
Every captain- and player-invoking state supports this path
|
|
642
|
+
Every captain- and player-invoking state supports this path, with one
|
|
643
|
+
exception the compiler shall apply, not infer: the states of a controller
|
|
644
|
+
machine ([Setup](#setup), controller decision-state class) carry no
|
|
645
|
+
Boss-reply suspension, because its hub already receives every Boss turn and a
|
|
646
|
+
clarifying question to Boss is a `respond` selection over the closed action
|
|
647
|
+
set. The rule below is therefore universal over workflow states and silent
|
|
648
|
+
about that class; in particular, adding `needsBossReply` to the controller
|
|
649
|
+
decision state would add a seventh outcome to a closed six-action contract
|
|
650
|
+
whose guard discriminants [Setup](#setup) fixes, and is nonconformant.
|
|
592
651
|
There is no source-level opt-in annotation and no `needsBossReply` result metadata in GEARS output.
|
|
593
652
|
The FSM compiler shall preserve the GEARS blockquote as the state's domain `prompt` body and shall not inject any Boss-question instruction into `invoke.input.prompt`.
|
|
653
|
+
This preservation includes every literal leading `>` carried inside the outer GEARS blockquote for quoted runtime context.
|
|
594
654
|
|
|
595
|
-
For every captain- and player-invoking state
|
|
655
|
+
For every captain- and player-invoking state outside a controller machine,
|
|
656
|
+
the compiler shall add
|
|
596
657
|
`needsBossReply` to the state's `invoke.input.result` map.
|
|
597
658
|
The description shall be the standard adjudicator-facing text:
|
|
598
659
|
|
package/slc/link.md
CHANGED
|
@@ -59,9 +59,17 @@ interface PlaybookSession {
|
|
|
59
59
|
parentSessionId?: string;
|
|
60
60
|
parentCallId?: string;
|
|
61
61
|
depth: number;
|
|
62
|
+
playerSessions?: PlayerSessionStore;
|
|
62
63
|
ports: PlaybookPorts;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
interface PlayerSessionStore {
|
|
67
|
+
select(playerId: string): string | false;
|
|
68
|
+
update(playerId: string, resumeToken?: string): void;
|
|
69
|
+
snapshot(): Readonly<Record<string, string>>;
|
|
70
|
+
restore(tokens: Readonly<Record<string, string>>): void;
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
type JsonValue =
|
|
66
74
|
| null
|
|
67
75
|
| boolean
|
|
@@ -143,7 +151,7 @@ exactly one `pendingCall` is active.
|
|
|
143
151
|
Control-plane exceptions reject the runtime method rather than masquerade as a
|
|
144
152
|
recoverable workflow `failed` result.
|
|
145
153
|
|
|
146
|
-
`PlaybookRuntimeOptions` is host-agnostic and carries only _per-run_ knobs such as identity strings (e.g., model names a playbook substitutes into prompt placeholders)
|
|
154
|
+
`PlaybookRuntimeOptions` is host-agnostic and carries only _per-run_ knobs such as identity strings (e.g., model names a playbook substitutes into prompt placeholders), strategy overrides the linker exposes, and — where the compiled playbook's policy needs a host seam — host-supplied port-shaped callbacks the linker exposes as option members whose types the artifact itself declares, so the six-member `PlaybookPorts` contract and the shared contract module stay free of host types.
|
|
147
155
|
The link compiler emits a typed options interface per playbook based on the FSM's `CodingInput` (or equivalent).
|
|
148
156
|
The CLI's absence of `--link-option` values does not mean that
|
|
149
157
|
`PlaybookRuntimeOptions` is empty. CLI link options are compile-time inputs;
|
|
@@ -240,6 +248,10 @@ type PlaybookCallStart =
|
|
|
240
248
|
|
|
241
249
|
`PlayerResult` mirrors the status, resume token, final text, and error fields of cligent's `PlayerRunResult` ([TMUX-033](https://github.com/sublang-ai/cligent/blob/main/specs/user/tmux-play.md#tmux-033)).
|
|
242
250
|
The runtime treats `status !== 'ok'` as a player failure and routes it through the FSM's error path (§Abort).
|
|
251
|
+
An `ok` player result whose `finalText` is missing, empty, or whitespace-only
|
|
252
|
+
earns exactly one corrective re-ask: the same player call repeated under the
|
|
253
|
+
stored resume selection, traced as its own player-call pair, before a second
|
|
254
|
+
such result routes through the same error path.
|
|
243
255
|
|
|
244
256
|
`callCaptain` runs a direct-Captain FSM actor against the host's Captain
|
|
245
257
|
agent. The linked runtime shall pass
|
|
@@ -257,9 +269,13 @@ array requests a tool-free call, while omission preserves the host Captain's
|
|
|
257
269
|
configured tools.
|
|
258
270
|
`CaptainResult` carries no resume token or player-continuation selection.
|
|
259
271
|
A non-`ok`
|
|
260
|
-
result, or an `ok` result
|
|
261
|
-
failure on the call's single finish trace
|
|
262
|
-
|
|
272
|
+
result, or an `ok` result whose `finalText` is missing, empty, or
|
|
273
|
+
whitespace-only, shall record that failure on the call's single finish trace.
|
|
274
|
+
A non-`ok` result shall then reject the actor through the FSM's error path
|
|
275
|
+
with no corrective re-ask; an empty `ok` result shall first earn exactly one
|
|
276
|
+
corrective re-ask — the same call repeated, traced as its own
|
|
277
|
+
started/finished pair — and only a second such result shall reject the actor
|
|
278
|
+
the same way. These structured host-result failures are recoverable
|
|
263
279
|
workflow failures, not control-plane failures: the runtime shall let the actor
|
|
264
280
|
take `onError`, drive it to quiescence, drain ordered emissions, and resolve
|
|
265
281
|
the public method with `{ outcome: 'failed' }` carrying the failure state's
|
|
@@ -347,6 +363,8 @@ type PlaybookTraceType =
|
|
|
347
363
|
| 'captain.call.finished'
|
|
348
364
|
| 'playbook.call.started'
|
|
349
365
|
| 'playbook.call.finished'
|
|
366
|
+
| 'apply.started'
|
|
367
|
+
| 'apply.finished'
|
|
350
368
|
| 'fsm.transition'
|
|
351
369
|
| 'status.emitted'
|
|
352
370
|
| 'boss.input.settled'
|
|
@@ -369,14 +387,28 @@ interface PlaybookTraceEvent {
|
|
|
369
387
|
}
|
|
370
388
|
```
|
|
371
389
|
|
|
390
|
+
A composing host may supply `playerSessions` as a frame-local view of player continuation owned by the root engagement tree.
|
|
391
|
+
The runtime shall select through that view before allocating or tracing a player call and shall update or clear it from a validated player result before emitting the matching finish trace.
|
|
392
|
+
Snapshot export and restore shall use the same view, failed restore shall leave its prior contents unchanged, and child disposal shall not clear host-owned continuation.
|
|
393
|
+
A host that omits the view retains the runtime's private per-session continuation behavior.
|
|
394
|
+
|
|
372
395
|
The trace types are `session.started`, `boss.input.received`,
|
|
373
396
|
`judge.call.started`, `judge.call.finished`, `player.call.started`,
|
|
374
397
|
`player.call.finished`, `captain.call.started`, `captain.call.finished`,
|
|
375
398
|
`playbook.call.started`,
|
|
376
|
-
`playbook.call.finished`, `
|
|
399
|
+
`playbook.call.finished`, `apply.started`, `apply.finished`,
|
|
400
|
+
`fsm.transition`, `status.emitted`,
|
|
377
401
|
`boss.input.settled`, and `session.disposed`.
|
|
378
402
|
Call pairs carry exact prompts and replies, normalized failures, actor and state
|
|
379
403
|
identity, and their boundary-specific options.
|
|
404
|
+
`apply.started` and `apply.finished` are the paired schema-2 boundary of an
|
|
405
|
+
executed `apply()` call on a runtime implementing the optional control surface
|
|
406
|
+
(§Control surface): both carry the action id and idempotency `key` (plus the
|
|
407
|
+
singular `stateId` on start when one exists), the pair shares one
|
|
408
|
+
session-unique `apply-<n>` call id and the boundary's turn id, and the finish
|
|
409
|
+
adds the receipt `disposition` with its `reason`, normalized `error`, or
|
|
410
|
+
projected `run` result, all JSON-safe. A repeated idempotency key returns the
|
|
411
|
+
recorded receipt without a new pair.
|
|
380
412
|
Direct-Captain start and finish payloads shall carry `allowedTools` exactly when
|
|
381
413
|
the originating `CaptainCallOptions` selects it and shall omit the member when
|
|
382
414
|
the call preserves the host Captain's configured tools.
|
|
@@ -392,16 +424,23 @@ its message and optional data; consumers shall not have to recover state
|
|
|
392
424
|
identity from a nested ad hoc object.
|
|
393
425
|
Judge results use `reply`; player start and finish payloads both carry the
|
|
394
426
|
selected `resume`; Captain start and finish payloads both carry
|
|
395
|
-
the exact composed prompt, `visibility
|
|
396
|
-
`stateId` and `sourceItem`, and no player resume selection or
|
|
427
|
+
the exact composed prompt, the boundary's selected `visibility`, the direct
|
|
428
|
+
invocation's `stateId` and `sourceItem`, and no player resume selection or
|
|
429
|
+
resume token — a visible workflow call carries its runtime-owned
|
|
430
|
+
`resume: false` selection, while a hidden controller call
|
|
431
|
+
(§Captain adjudication) omits the `resume` member altogether, its
|
|
432
|
+
durable-conversation selection being host-owned;
|
|
397
433
|
judge `purpose` is
|
|
398
434
|
`boss-input-classification`, `player-output-adjudication`, or
|
|
399
435
|
`captain-output-adjudication`; and every error uses
|
|
400
436
|
`{ name, message, stack? }` rather than a raw string or `Error` instance.
|
|
401
437
|
The Captain finish payload shall preserve the exact `CaptainResult` status and
|
|
402
438
|
final text when present, while carrying any failure in normalized form.
|
|
403
|
-
An `ok` result without `finalText` therefore retains status `ok` but
|
|
404
|
-
carries the normalized missing-text failure
|
|
439
|
+
An `ok` result without non-empty `finalText` therefore retains status `ok` but
|
|
440
|
+
also carries the normalized missing-text failure; the corrective re-ask that
|
|
441
|
+
follows (§PlaybookPorts contract) traces as its own started/finished pair, and,
|
|
442
|
+
when that corrective call happens, only a second such finish makes the actor
|
|
443
|
+
reject.
|
|
405
444
|
If the Captain port rejects before returning a result, the finish instead
|
|
406
445
|
carries explicit `status: 'aborted'` when the combined signal has aborted or
|
|
407
446
|
`status: 'error'` otherwise. A finish boundary never omits status merely
|
|
@@ -505,6 +544,7 @@ decides only the `callPlayer` invocation.
|
|
|
505
544
|
The runtime shall compose the actual player prompt from the state's
|
|
506
545
|
`PlayerInput`.
|
|
507
546
|
`input.prompt` is the GEARS-derived domain prompt body and shall not be mutated, re-flowed, or treated as a place to store framework control instructions.
|
|
547
|
+
A leading `>` inside that body is authored quoted-context content and shall reach the player unchanged.
|
|
508
548
|
|
|
509
549
|
The composer may prepend structured labelled blocks from typed `PlayerInput`
|
|
510
550
|
fields the FSM exposes (for example `Boss intent:`, `Review items:`,
|
|
@@ -596,8 +636,17 @@ Where the current ready or reconstructed terminal machine accepts exactly one
|
|
|
596
636
|
ordinary textual entry event and no Boss question is pending, the runtime
|
|
597
637
|
shall send that event deterministically and attach the exact original text to
|
|
598
638
|
its declared textual payload field without invoking `callJudge`.
|
|
599
|
-
The default Captain
|
|
600
|
-
|
|
639
|
+
The default Captain — the controller playbook of
|
|
640
|
+
[gears2fsm "Setup"](gears2fsm.md#setup) — is deterministic at every parked
|
|
641
|
+
entry: the runtime maps each Boss turn from the exact text and the host's
|
|
642
|
+
deterministic command-parse resolution, supplied through the linked options'
|
|
643
|
+
controller port, to the rewritten machine's hub entry union —
|
|
644
|
+
`{ type: 'BOSS_TURN', bossText: turn.text }` for an undecided turn,
|
|
645
|
+
`{ type: 'PARSED_RESPOND', bossText: turn.text }` for a parse-resolved
|
|
646
|
+
`respond`, `{ type: 'PARSED_ACTION', bossText: turn.text, decision }` carrying
|
|
647
|
+
the injected parse-resolved decision object, and `{ type: 'SHUTDOWN' }` for the
|
|
648
|
+
host's teardown resolution — and invokes no classifier judge call; the exact
|
|
649
|
+
original text still rides only the runtime-owned textual payload field.
|
|
601
650
|
All other non-empty turns shall use `callJudge` only to choose one of the FSM's
|
|
602
651
|
event kinds and non-text routing fields, or no FSM action.
|
|
603
652
|
The classifier prompt shall include the exact, unmodified `turn.text` in a
|
|
@@ -677,7 +726,7 @@ If a host forwards text beginning with `/` to `handleBossInput`, the runtime tre
|
|
|
677
726
|
Hosts that receive structured control input shall resolve host-level concerns before choosing a playbook runtime.
|
|
678
727
|
Once they call `handleBossInput`, they shall pass the Boss content as text and shall not pre-classify in-playbook FSM events or rely on slash forms as a runtime protocol.
|
|
679
728
|
|
|
680
|
-
`BOSS_INTERRUPT` (or the FSM's equivalent explicit-state-jump event) is reached only by the judge choosing it and supplying its required target payload.
|
|
729
|
+
Within `handleBossInput` classification, `BOSS_INTERRUPT` (or the FSM's equivalent explicit-state-jump event) is reached only by the judge choosing it and supplying its required target payload; `apply()` of a runtime-advertised action (§Control surface) is the second, runtime-validated path to the same events, and on neither path does the host fabricate an FSM event itself.
|
|
681
730
|
It is _not_ an abort surface; aborts go through the abort signal and the strategies in §Abort.
|
|
682
731
|
Hosts where the abort signal is terminal (e.g., SIGINT runs shutdown) shall not route abort to `BOSS_INTERRUPT`.
|
|
683
732
|
|
|
@@ -696,6 +745,9 @@ clause (or equivalent typed output metadata). Backticked prose before that
|
|
|
696
745
|
clause can name statuses, guards, or concepts such as `ok`, `aborted`, and
|
|
697
746
|
`error`; those names are not output properties and shall never become required
|
|
698
747
|
judge fields.
|
|
748
|
+
For a delegated-player field annotated exactly `` `<field>: <verbatim final text>` ``, the judge shall select the guard but the runtime shall replace any judge-supplied value with the player's canonical non-empty final text before returning the actor output.
|
|
749
|
+
The linker shall derive the complete `verbatimPayloadFields` set from those annotations across the FSM result maps.
|
|
750
|
+
A field name that is annotated in one result map and unannotated in another is a link error because the shared adjudication strategy cannot give one property both ownership policies.
|
|
699
751
|
For a direct Captain result, `question` and `response` are human-presentation
|
|
700
752
|
fields owned by the visible call rather than fields authored by the hidden
|
|
701
753
|
judge.
|
|
@@ -746,6 +798,55 @@ presentation. The linked
|
|
|
746
798
|
runtime shall not make a second visible Captain call or expose the hidden
|
|
747
799
|
structured adjudication merely to present the same response.
|
|
748
800
|
|
|
801
|
+
That visible-call presentation — visible Captain prose as the Boss
|
|
802
|
+
presentation, a separate hidden adjudicator that never authors the
|
|
803
|
+
`question`/`response` fields, and runtime injection of the visible
|
|
804
|
+
`finalText` — stays scoped to a working playbook whose FSM declares a visible
|
|
805
|
+
direct-Captain state. For a controller playbook, whose FSM declares the controller
|
|
806
|
+
decision-state class of [gears2fsm "Setup"](gears2fsm.md#setup), the
|
|
807
|
+
Captain-call presentation admits the hidden controller form instead
|
|
808
|
+
(DR-029): the decision and closing-reply Captain calls run
|
|
809
|
+
`{ visibility: 'hidden' }` on the host's durable conversation, whose resume
|
|
810
|
+
token the host pins and rotates (DR-029), and the decision call's reply
|
|
811
|
+
is the `{ action, … }` control JSON itself — validated by the linked runtime
|
|
812
|
+
against the declared decision-state contract rather than adjudicated through
|
|
813
|
+
a separate judge call, with exactly one corrective re-ask appending the
|
|
814
|
+
rejection reason and the restated reply contract (the DR-025 corrective
|
|
815
|
+
pattern). Because the host owns that conversation, the `resume` member the
|
|
816
|
+
runtime is required to pass on a controller call carries no continuity
|
|
817
|
+
meaning: the runtime passes `resume: false` because it holds no token, and
|
|
818
|
+
the host's pinned durable selection overrides it — a controller call shall
|
|
819
|
+
never be read as a request for a fresh conversation.
|
|
820
|
+
|
|
821
|
+
The validated selection is not itself an effect. The linked runtime shall
|
|
822
|
+
submit it through the host-supplied controller port the linker exposed as an
|
|
823
|
+
option member (§PlaybookRuntime contract) and shall take the returned
|
|
824
|
+
settlement as the only evidence of what happened. That settlement, carried
|
|
825
|
+
beside the selected action as the guard discriminant, is the decision
|
|
826
|
+
invocation's own result — the actor output the decision state's `onDone` arms
|
|
827
|
+
select on and its evidence action records — so the effect reports through the
|
|
828
|
+
same invocation that decided it, with no second boundary and no host-sent
|
|
829
|
+
event. A controller prose state (a parse-resolved `respond` reply, an acting
|
|
830
|
+
turn's closing reply) settles on its declared single outcome and returns no
|
|
831
|
+
prose to the machine at all.
|
|
832
|
+
|
|
833
|
+
That reply is control data, never Boss presentation: the runtime
|
|
834
|
+
shall not inject the `question` or `response` presentation fields into a
|
|
835
|
+
controller result — no visible Captain call exists to own them. Controller
|
|
836
|
+
prose reaches the Boss only as host-validated captain speech surfaced
|
|
837
|
+
through the host's presentation seam, cligent `CaptainContext.emitReply`
|
|
838
|
+
(DR-029). Because no prose returns to the machine and no presentation
|
|
839
|
+
field is injected, the host's own `callCaptain` implementation is that seam:
|
|
840
|
+
it already holds the `CaptainResult` of the call it just served, and it
|
|
841
|
+
identifies which call that is from the paired `captain.call.started` boundary
|
|
842
|
+
the runtime emits before invoking the port, whose identity carries the
|
|
843
|
+
invoking `stateId` and `sourceItem` (§Playbook trace). No prose therefore
|
|
844
|
+
needs a return path through the machine, and none exists.
|
|
845
|
+
The visible-call `question`/`response` injection rule above and
|
|
846
|
+
the `{ visibility: 'visible', resume: false }` workflow-call selection
|
|
847
|
+
(§PlaybookPorts contract, §Captain prompt composition) stay the
|
|
848
|
+
visible-presentation shape for non-controller playbooks.
|
|
849
|
+
|
|
749
850
|
The adjudicator shall fail loudly on:
|
|
750
851
|
|
|
751
852
|
- A guard the state does not declare,
|
|
@@ -791,9 +892,9 @@ independent cleanup evidence.
|
|
|
791
892
|
## Script execution
|
|
792
893
|
|
|
793
894
|
Where the FSM declares the typed `script` actor from
|
|
794
|
-
[gears2fsm "Setup"](gears2fsm.md#setup),
|
|
795
|
-
its implementation through the shared factory (§Output); the linker
|
|
796
|
-
not regenerate a script executor inside
|
|
895
|
+
[gears2fsm "Setup"](gears2fsm.md#setup), a factory-backed linked runtime shall
|
|
896
|
+
provide its implementation through the shared factory (§Output); the linker
|
|
897
|
+
shall not regenerate a script executor inside a factory-backed emitted module.
|
|
797
898
|
A script invocation is the one actor kind that runs without any agent:
|
|
798
899
|
it makes no `callPlayer`, `callCaptain`, or `callJudge` call and needs no
|
|
799
900
|
adjudication.
|
|
@@ -1090,6 +1191,23 @@ The `PlaybookRuntime` shall:
|
|
|
1090
1191
|
failure cannot skip the parent disposal boundary or leave the runtime bound.
|
|
1091
1192
|
|
|
1092
1193
|
The actor's `lastError` field shall be surfaced via `emitStatus` when the machine enters its `failed` state.
|
|
1194
|
+
Presence of linker-emitted `playerStates` selects the canonical
|
|
1195
|
+
factory-backed status profile. That profile shall emit the selected Boss event type
|
|
1196
|
+
before sending that event, exactly `→ <guard>` (with no payload-count or tally
|
|
1197
|
+
rider) when a settling actor output carries a guard, and
|
|
1198
|
+
`⤷ <Player>: <label>` only when the entered state appears in the
|
|
1199
|
+
linked module's `playerStates` metadata. It shall emit no raw state-id fallback
|
|
1200
|
+
for any other state. `playerStates` shall be a complete map of the FSM states
|
|
1201
|
+
that invoke the typed `player` actor; each value carries the exact player from
|
|
1202
|
+
that state's source-derived `meta.playbook.player` and the state's exact FSM
|
|
1203
|
+
description as `{ player, label }`. The factory shall reject an incomplete
|
|
1204
|
+
entry, a non-player state, or a player or label that differs from the FSM
|
|
1205
|
+
metadata. A schema-1 legacy module that predates this metadata
|
|
1206
|
+
shall preserve the prior factory status defaults, including no classification
|
|
1207
|
+
line, `Entered <stateId>.` for ordinary state entry, the single question line,
|
|
1208
|
+
and the unglyphed failure line. The new profile is opt-in so adding the seam
|
|
1209
|
+
does not silently reinterpret an already-linked artifact without an ABI or
|
|
1210
|
+
artifact-schema bump.
|
|
1093
1211
|
For the default Captain runtime, an initial `ready` state and a terminal `done`
|
|
1094
1212
|
state shall not emit human status. The terminal response is already visible
|
|
1095
1213
|
Captain prose; a synthetic “entered done” message would present it twice.
|
|
@@ -1170,6 +1288,162 @@ not `active` or whose state descriptor cannot be normalized shall fail
|
|
|
1170
1288
|
A restore failure shall leave the runtime unbound so `dispose` remains
|
|
1171
1289
|
callable and terminal.
|
|
1172
1290
|
|
|
1291
|
+
## Control surface (optional)
|
|
1292
|
+
|
|
1293
|
+
A linked runtime may implement the optional control-surface capability of
|
|
1294
|
+
`@sublang/playbook/runtime` — `describe()` and `apply(...)` — so a host can
|
|
1295
|
+
observe the parked machine and execute a runtime-advertised recovery or jump
|
|
1296
|
+
action without fabricating an FSM event (DR-029). A runtime that
|
|
1297
|
+
implements either member shall implement both; every runtime the shared
|
|
1298
|
+
`createXStatePlaybookRuntime` factory constructs implements the pair. A
|
|
1299
|
+
runtime lacking the pair advertises no actions, and plain text delivery is
|
|
1300
|
+
the only verb against it. Presence is feature-detected like the
|
|
1301
|
+
parked-session snapshot capability; the pair changes no runtime ABI and no
|
|
1302
|
+
artifact or snapshot schema.
|
|
1303
|
+
|
|
1304
|
+
```typescript
|
|
1305
|
+
interface PlaybookControlAction {
|
|
1306
|
+
id: string; // stable within the returned view
|
|
1307
|
+
label: string; // runtime-written, Boss-appropriate
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
interface PlaybookControlView {
|
|
1311
|
+
state: PlaybookState;
|
|
1312
|
+
stateDescription?: string; // runtime-published meaning of the current state
|
|
1313
|
+
context?: JsonValue; // the runtime's authored projection, sanitized
|
|
1314
|
+
pendingQuestions: readonly PlaybookPendingBossQuestion[];
|
|
1315
|
+
lastError?: NormalizedError;
|
|
1316
|
+
actions: readonly PlaybookControlAction[];
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
type PlaybookControlReceipt =
|
|
1320
|
+
| { disposition: 'rejected'; reason: string } // before any effect
|
|
1321
|
+
| { disposition: 'executed'; run: PlaybookRunResult }
|
|
1322
|
+
| { disposition: 'failed'; error: NormalizedError }; // effects may exist
|
|
1323
|
+
|
|
1324
|
+
// Optional PlaybookRuntime members — both or neither:
|
|
1325
|
+
describe?(): PlaybookControlView;
|
|
1326
|
+
apply?(input: { actionId: string; key: string; signal: AbortSignal }): Promise<PlaybookControlReceipt>;
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
`describe()` shall be side-effect free — it emits no trace, status, or
|
|
1330
|
+
telemetry and moves no machine state — and is valid at parked quiescence
|
|
1331
|
+
outside an active `handleBossInput`/`resumePlaybookCall`/`apply` boundary;
|
|
1332
|
+
during an active boundary, before `init`, or after disposal begins it shall
|
|
1333
|
+
throw. The view carries the current normalized state descriptor, the state
|
|
1334
|
+
description defined below, the authored context projection defined below, the
|
|
1335
|
+
pending Boss questions with their stable ids, the last recorded error in
|
|
1336
|
+
normalized form, and the currently valid actions.
|
|
1337
|
+
|
|
1338
|
+
`stateDescription` is the runtime's own Boss-facing statement of what its
|
|
1339
|
+
current state means, taken from the same source state descriptions the action
|
|
1340
|
+
labels below are written from. A controller host has no other grounding for a
|
|
1341
|
+
status answer, and an internal state id is not text a reply may repeat, so the
|
|
1342
|
+
runtime publishes the meaning rather than leaving the host to substitute the
|
|
1343
|
+
identifier for it. A state whose source declares no description carries no
|
|
1344
|
+
`stateDescription`: an id is never promoted into a description, so a host is
|
|
1345
|
+
never handed an identifier dressed as meaning.
|
|
1346
|
+
|
|
1347
|
+
The view's `context` is an explicit projection the linked runtime **authors**,
|
|
1348
|
+
never an allow-by-default serialization of the FSM context (PBRT-52).
|
|
1349
|
+
Only the runtime knows which of its context members are safe and relevant
|
|
1350
|
+
for a controller prompt, while the host receiving the view cannot inspect an
|
|
1351
|
+
opaque blob for the player rosters, option values, and raw player output its
|
|
1352
|
+
own prompts must exclude; exporting by default makes the two obligations
|
|
1353
|
+
unsatisfiable together and gives every member added to an FSM later the wrong
|
|
1354
|
+
default. The rules:
|
|
1355
|
+
|
|
1356
|
+
- The emitted module declares the projection in its `spec` as
|
|
1357
|
+
`controlContextFields` — the FSM context member names its view exposes, in
|
|
1358
|
+
the order it names them — and the factory exports those and nothing else.
|
|
1359
|
+
- A runtime naming no member carries no `context` at all, so a member is
|
|
1360
|
+
private until an artifact names it and extending an FSM leaks nothing by
|
|
1361
|
+
omission.
|
|
1362
|
+
- Sanitization sits on top of the projection, not in place of it: a named
|
|
1363
|
+
member is still normalized (raw `Error` values normalized) and dropped when
|
|
1364
|
+
it cannot be made JSON-safe, rather than thrown, since `describe` stays
|
|
1365
|
+
side-effect free and total.
|
|
1366
|
+
- The two members the view surfaces first-class — the pending Boss question
|
|
1367
|
+
and the last error — cannot be named. A projection naming either is a
|
|
1368
|
+
construction error, failing runtime construction rather than being silently
|
|
1369
|
+
ignored.
|
|
1370
|
+
- The host still composes its own prompt block from the projection rather than
|
|
1371
|
+
pasting the projection in, so no runtime's exported value can forge a block
|
|
1372
|
+
into an envelope the host owns.
|
|
1373
|
+
|
|
1374
|
+
Actions derive from the live snapshot, only at the same safe point the
|
|
1375
|
+
parked-session snapshot uses (actor status `active`, quiescent, no pending
|
|
1376
|
+
nested call); anywhere else `actions` is empty while the rest of the view
|
|
1377
|
+
still describes the state. Two families exist, labeled from source state
|
|
1378
|
+
descriptions:
|
|
1379
|
+
|
|
1380
|
+
- **Failure-state retry** — while the singular state id is the recoverable
|
|
1381
|
+
failure state and the runtime holds a recorded last classified event (the
|
|
1382
|
+
event a public Boss boundary sent that drove the run into `failed`, kept
|
|
1383
|
+
with its recorded payload), and the live snapshot accepts that event, the
|
|
1384
|
+
runtime shall advertise `retry:<EVENT_TYPE>` replaying exactly that
|
|
1385
|
+
recorded event.
|
|
1386
|
+
- **Jump entries** — for each registered resumable state id whose
|
|
1387
|
+
explicit-state-jump event (`BOSS_INTERRUPT` with that `targetId`, optional
|
|
1388
|
+
textual fields omitted) the live snapshot accepts, guards included, the
|
|
1389
|
+
runtime shall advertise `jump:<stateId>`.
|
|
1390
|
+
|
|
1391
|
+
A candidate whose event requires a payload the runtime cannot source from
|
|
1392
|
+
recorded state shall be excluded from `actions` — `apply` never invents free
|
|
1393
|
+
text and never enters Boss-input classification. A candidate whose *label*
|
|
1394
|
+
could only be an identifier is excluded on the same terms: a label never falls
|
|
1395
|
+
back to a target id or to the replayed event type, because a controller host
|
|
1396
|
+
names an executed or refused action by its label and never by its id, so an
|
|
1397
|
+
identifier used as a label defeats that substitution. A jump whose target
|
|
1398
|
+
publishes no description is therefore not advertised — borrowing another
|
|
1399
|
+
state's description would name the wrong state — and a retry falls back from
|
|
1400
|
+
its target's description to its own source state's, and is not advertised when
|
|
1401
|
+
neither exists.
|
|
1402
|
+
|
|
1403
|
+
`apply({ actionId, key, signal })` shall revalidate the action against the
|
|
1404
|
+
live state and settle `{ disposition: 'rejected', reason }` with no effect
|
|
1405
|
+
when it is no longer advertised. It shall execute an accepted action at most
|
|
1406
|
+
once per idempotency `key`: the receipt is recorded at acceptance, before
|
|
1407
|
+
the settlement emissions, and a repeated key returns the recorded receipt
|
|
1408
|
+
verbatim with no revalidation, no execution, and no new trace pair within the
|
|
1409
|
+
runtime instance.
|
|
1410
|
+
Only accepted receipts (`executed` or `failed`) are recorded and final for
|
|
1411
|
+
their key. A rejection settles before acceptance and records nothing under
|
|
1412
|
+
its key — a later call with that key revalidates afresh, traces its own
|
|
1413
|
+
pair, and may execute once the action is advertised — and a key whose call
|
|
1414
|
+
threw before reaching acceptance (lifecycle misuse, invalid input, a
|
|
1415
|
+
pre-acceptance abort, a rejected start-boundary sink) likewise records
|
|
1416
|
+
nothing, so a later call with that key may execute. Executing sends the
|
|
1417
|
+
validated event through the same actor drive as `handleBossInput` — state
|
|
1418
|
+
transitions, player/judge boundaries, statuses, and traces flow unchanged —
|
|
1419
|
+
and settles `executed` with the projected run result, or `failed` with the
|
|
1420
|
+
normalized error when the run settles in the failure state, aborts, or a
|
|
1421
|
+
post-acceptance control-plane error lands (effects may exist). `signal`
|
|
1422
|
+
follows §Abort exactly as a Boss-turn signal does; an abort after acceptance
|
|
1423
|
+
settles the `failed` receipt rather than rejecting. The boundary traces as
|
|
1424
|
+
the paired `apply.started` / `apply.finished` events of §Playbook trace, and
|
|
1425
|
+
`apply` shares the single active-boundary sentinel with `handleBossInput`
|
|
1426
|
+
and `resumePlaybookCall`.
|
|
1427
|
+
|
|
1428
|
+
Acceptance is also the line past which `apply` does not throw, and
|
|
1429
|
+
publication — the `apply.finished` emission — is the line past which its
|
|
1430
|
+
receipt no longer changes. A settlement failure after acceptance but before
|
|
1431
|
+
publication (a rejecting emission drain) settles the `failed` receipt carrying
|
|
1432
|
+
its normalized error, replacing the one recorded at acceptance so the finish
|
|
1433
|
+
trace, the returned receipt, and any replay of the key report one settlement.
|
|
1434
|
+
A settlement failure at or after publication (a rejecting `apply.finished`
|
|
1435
|
+
sink) does not: the disposition is already emitted, so no rewrite can make the
|
|
1436
|
+
trace and the return agree, and a receipt states what happened to the effect
|
|
1437
|
+
rather than what happened to its telemetry. The published receipt stands, is
|
|
1438
|
+
returned and replayed verbatim, and the delivery failure travels on the
|
|
1439
|
+
runtime's emission-failure channel to surface from the next public boundary
|
|
1440
|
+
that drains.
|
|
1441
|
+
|
|
1442
|
+
The recorded receipts and the recorded last classified event are
|
|
1443
|
+
process-local: the schema-1 parked-session snapshot persists neither, and a
|
|
1444
|
+
restored runtime advertises a retry again only after its next classified
|
|
1445
|
+
event.
|
|
1446
|
+
|
|
1173
1447
|
## Abort
|
|
1174
1448
|
|
|
1175
1449
|
`handleBossInput.signal` is the abort surface.
|
|
@@ -1245,19 +1519,25 @@ The `playbook.trace` copies are the host-agnostic runtime-boundary record requir
|
|
|
1245
1519
|
|
|
1246
1520
|
## Output
|
|
1247
1521
|
|
|
1248
|
-
The link compiler emits
|
|
1522
|
+
The link compiler emits one TypeScript module per playbook.
|
|
1523
|
+
For an FSM that declares no `type: 'parallel'` state, it shall emit the thin
|
|
1524
|
+
shared-factory module defined below.
|
|
1525
|
+
For an FSM that declares a parallel state, it shall emit bespoke linked
|
|
1526
|
+
machinery satisfying this document's runtime contract and shall not invoke
|
|
1527
|
+
`createXStatePlaybookRuntime`, whose supported domain is single-region FSMs
|
|
1528
|
+
under [DR-019](../specs/decisions/019-shared-linked-runtime-factory.md).
|
|
1249
1529
|
The FSM-interpreter machinery — actor wiring, boundary tracing, Boss-event
|
|
1250
1530
|
mapping, adjudication, script execution, nested-playbook bridging, session
|
|
1251
1531
|
lifecycle, abort handling, and the optional parked-session snapshot
|
|
1252
|
-
capability — is not regenerated
|
|
1253
|
-
`createXStatePlaybookRuntime(machine, spec)` factory exported by
|
|
1532
|
+
capability — is not regenerated for a factory-backed artifact: it ships once
|
|
1533
|
+
as the shared `createXStatePlaybookRuntime(machine, spec)` factory exported by
|
|
1254
1534
|
`@sublang/playbook/xstate-runtime`, and the emitted module hands its FSM and
|
|
1255
1535
|
a small per-playbook `spec` to that factory. Every behavioral section of
|
|
1256
1536
|
this definition still binds the emitted module's runtime; the shared factory
|
|
1257
1537
|
is how the emitted module satisfies them, so a runtime fix ships as a
|
|
1258
1538
|
package release instead of a re-link of every artifact.
|
|
1259
1539
|
|
|
1260
|
-
The emitted module:
|
|
1540
|
+
The thin emitted module:
|
|
1261
1541
|
|
|
1262
1542
|
- Imports the FSM artifact by relative path with an extension-bearing
|
|
1263
1543
|
runtime specifier. When the linked TypeScript is part of a package that
|
|
@@ -1297,10 +1577,28 @@ The emitted module:
|
|
|
1297
1577
|
exceptions not covered by the canonical kebab-token-to-camel-field mapping
|
|
1298
1578
|
and the canonical `<#>` → `irNumber` special case; the
|
|
1299
1579
|
transition-event payload fields the FSM's Boss union declares; a
|
|
1300
|
-
non-default player binding where the linker inputs supplied one;
|
|
1580
|
+
non-default player binding where the linker inputs supplied one; the
|
|
1581
|
+
complete `playerStates` status map derived from every FSM state that invokes
|
|
1582
|
+
the typed `player` actor, with each `player` copied from that state's
|
|
1583
|
+
source-derived `meta.playbook.player` (an empty map when there is no such
|
|
1584
|
+
state); the
|
|
1585
|
+
`verbatimPayloadFields` set derived from annotated result fields above; the
|
|
1586
|
+
`controlContextFields` projection of §Control surface; and any
|
|
1301
1587
|
per-playbook strategy override (classifier, prompt composers,
|
|
1302
1588
|
required-field extraction, status formatting) an earlier section of this
|
|
1303
|
-
definition requires for that playbook.
|
|
1589
|
+
definition requires for that playbook.
|
|
1590
|
+
`controlContextFields` is authored, not derived: the linker names the FSM
|
|
1591
|
+
context members the playbook's controller view exposes and no others, in the
|
|
1592
|
+
order the view should render them, omitting the member entirely where the
|
|
1593
|
+
playbook exposes no context. It is the one spec member whose default is
|
|
1594
|
+
*nothing* rather than everything — the factory exports no context for a
|
|
1595
|
+
module that supplies none — so a module emitted without it advertises a
|
|
1596
|
+
playbook with no Boss-visible context rather than one whose whole FSM
|
|
1597
|
+
context is Boss-visible. The linker shall not name a member the view
|
|
1598
|
+
surfaces first-class (the pending Boss question, the last error), which is a
|
|
1599
|
+
construction error, and shall not name a member carrying a resolved player
|
|
1600
|
+
roster, an option value, or player-authored text, which a controller host's
|
|
1601
|
+
prompts are required to exclude or to fence. The metadata shall keep the shared
|
|
1304
1602
|
classifier's reply contract exactly flat `{ type, ...declaredFields }` and
|
|
1305
1603
|
distinguish judge-authored routing fields from exact-text fields the
|
|
1306
1604
|
runtime attaches itself. Everything else — player/script/captain/nested actor
|
|
@@ -1320,7 +1618,13 @@ The emitted module:
|
|
|
1320
1618
|
fields?: Readonly<Record<string, XStateBossEventFieldSpec>>;
|
|
1321
1619
|
}
|
|
1322
1620
|
|
|
1621
|
+
interface XStatePlayerStateStatus {
|
|
1622
|
+
player: string;
|
|
1623
|
+
label: string;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1323
1626
|
bossEvents?: readonly XStateBossEventSpec[];
|
|
1627
|
+
playerStates?: Readonly<Record<string, XStatePlayerStateStatus>>;
|
|
1324
1628
|
placeholderFields?: Readonly<Record<string, string>>;
|
|
1325
1629
|
```
|
|
1326
1630
|
|
|
@@ -1352,10 +1656,16 @@ The emitted module:
|
|
|
1352
1656
|
- Default-exports the factory call as `createPlaybookRuntime`, typed
|
|
1353
1657
|
`PlaybookRuntimeFactory<PlaybookRuntimeOptions>`.
|
|
1354
1658
|
- Exposes, under an `_internal` export, the pure helpers verification
|
|
1355
|
-
needs — at least the
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1659
|
+
needs — at least the prompt composers its own machine uses, which may
|
|
1660
|
+
re-export the shared defaults when the spec does not override composition —
|
|
1661
|
+
so compilation-correctness tests can exercise composition without a host.
|
|
1662
|
+
A playbook that calls players exposes `composePlayerPrompt`; a playbook
|
|
1663
|
+
whose states make direct-Captain calls exposes `composeCaptainPrompt`. A
|
|
1664
|
+
controller playbook that calls no players exposes no player composer:
|
|
1665
|
+
there is no composition to verify, and a stub under that name would
|
|
1666
|
+
describe work the module cannot do. `_internal` is not a public API — the
|
|
1667
|
+
leading underscore says so — and nothing in it is semver-stable; a helper
|
|
1668
|
+
a host is meant to call is a top-level export and is governed as one.
|
|
1359
1669
|
- Holds no host-specific types and no host primitive calls. The runtime
|
|
1360
1670
|
speaks only `PlaybookPorts` for every agent and host concern; the
|
|
1361
1671
|
`node:child_process` dependency of §Script execution lives in the shared
|
|
@@ -1364,6 +1674,7 @@ The emitted module:
|
|
|
1364
1674
|
top-of-file header comment so the file is reproducible from the same
|
|
1365
1675
|
inputs.
|
|
1366
1676
|
- Sources the contract types (`PlayerResult`, `PlayerCallOptions`,
|
|
1677
|
+
`PlayerSessionStore`,
|
|
1367
1678
|
`CaptainResult`, `CaptainCallOptions`, `PlaybookPorts`, `PlaybookSession`,
|
|
1368
1679
|
`PlaybookTraceEvent`,
|
|
1369
1680
|
`PlaybookCallRequest`, `PlaybookCallResult`, `PlaybookCallStart`,
|
|
@@ -1375,6 +1686,9 @@ The emitted module:
|
|
|
1375
1686
|
dependency runs one way — from each linked module to the shared
|
|
1376
1687
|
engine and contract, never the reverse.
|
|
1377
1688
|
|
|
1689
|
+
Both output profiles remain subject to the behavioral sections above and the
|
|
1690
|
+
verification requirements below.
|
|
1691
|
+
|
|
1378
1692
|
When a co-located integration test for the linked runtime already exists, the
|
|
1379
1693
|
link compiler shall run it before reporting success and treat any failure as a
|
|
1380
1694
|
generation failure. It shall not delete, skip, or weaken that suite to make a
|