@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.
- package/README.md +17 -4
- package/docs/cli.md +74 -29
- package/docs/configuration.md +209 -112
- package/docs/embedding.md +71 -25
- package/package.json +4 -3
- package/reference/sdlc/captain.playbook/captain.playbook.js +3 -3
- package/reference/sdlc/captain.playbook/captain.playbook.ts +3 -3
- package/reference/sdlc/code.md +1 -1
- package/reference/sdlc/code.playbook/bin/interactive-session.js +816 -0
- package/reference/sdlc/code.playbook/bin/launch-config.js +1078 -116
- package/reference/sdlc/code.playbook/bin/playbook.js +489 -34
- package/reference/sdlc/code.playbook/bin/run.js +283 -298
- package/reference/sdlc/code.playbook/bin/session-store.js +818 -26
- package/reference/sdlc/code.playbook/code.fsm.d.ts +5 -5
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +2 -2
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +2 -2
- package/reference/sdlc/code.playbook/code.fsm.js +7 -11
- package/reference/sdlc/code.playbook/code.fsm.ts +9 -17
- package/reference/sdlc/code.playbook/code.gears.md +1 -1
- package/reference/sdlc/code.playbook/code.playbook.d.ts +2 -1
- package/reference/sdlc/code.playbook/code.playbook.js +12 -13
- package/reference/sdlc/code.playbook/code.playbook.ts +22 -15
- package/reference/sdlc/code.playbook/code.registry.d.ts +5 -13
- package/reference/sdlc/code.playbook/code.registry.js +3 -10
- package/reference/sdlc/code.playbook/code.registry.ts +7 -32
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +39 -14
- package/reference/sdlc/code.playbook/playbook-captain.js +970 -289
- package/reference/sdlc/code.playbook/playbook-captain.ts +1403 -396
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +41 -49
- package/reference/sdlc/decide.md +4 -4
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +9 -9
- package/reference/sdlc/decide.playbook/decide.fsm.js +21 -14
- package/reference/sdlc/decide.playbook/decide.fsm.ts +27 -23
- package/reference/sdlc/decide.playbook/decide.gears.md +3 -5
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +9 -13
- package/reference/sdlc/decide.playbook/decide.playbook.js +171 -134
- package/reference/sdlc/decide.playbook/decide.playbook.ts +238 -162
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +5 -13
- package/reference/sdlc/decide.playbook/decide.registry.js +3 -9
- package/reference/sdlc/decide.playbook/decide.registry.ts +7 -31
- package/reference/sdlc/review.md +4 -5
- package/reference/sdlc/review.playbook/review.fsm.d.ts +9 -11
- package/reference/sdlc/review.playbook/review.fsm.js +30 -24
- package/reference/sdlc/review.playbook/review.fsm.ts +39 -35
- package/reference/sdlc/review.playbook/review.gears.md +6 -5
- package/reference/sdlc/review.playbook/review.playbook.d.ts +2 -1
- package/reference/sdlc/review.playbook/review.playbook.js +16 -21
- package/reference/sdlc/review.playbook/review.playbook.ts +26 -26
- package/reference/sdlc/review.playbook/review.registry.d.ts +5 -13
- package/reference/sdlc/review.playbook/review.registry.js +3 -16
- package/reference/sdlc/review.playbook/review.registry.ts +7 -38
- package/slc/gears2fsm.md +27 -23
- package/slc/link.md +113 -93
- package/slc/text2gears.md +19 -18
- package/src/runtime.d.ts +20 -16
- package/src/runtime.ts +19 -23
- package/src/xstate-playbook-runtime.d.ts +21 -17
- package/src/xstate-playbook-runtime.js +241 -149
- package/src/xstate-playbook-runtime.ts +331 -178
- package/src/xstate-runtime.js +63 -24
- package/src/xstate-runtime.ts +96 -28
package/slc/link.md
CHANGED
|
@@ -59,13 +59,27 @@ interface PlaybookSession {
|
|
|
59
59
|
parentSessionId?: string;
|
|
60
60
|
parentCallId?: string;
|
|
61
61
|
depth: number;
|
|
62
|
+
roleBindings?: Readonly<Record<string, PlaybookRoleBinding>>;
|
|
62
63
|
playerSessions?: PlayerSessionStore;
|
|
63
64
|
ports: PlaybookPorts;
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
interface PlaybookRoleBinding {
|
|
68
|
+
readonly playerId: string;
|
|
69
|
+
readonly promptIdentity: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface PlaybookPendingBossQuestion {
|
|
73
|
+
questionId: string;
|
|
74
|
+
asker: { kind: 'captain' } | { kind: 'role'; roleId: string };
|
|
75
|
+
question: string;
|
|
76
|
+
sourceItem?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
interface PlayerSessionStore {
|
|
67
|
-
select(
|
|
68
|
-
|
|
80
|
+
select(roleId: string): string | false;
|
|
81
|
+
// Called only for a replacement token or an authorized ok-status clear.
|
|
82
|
+
update(roleId: string, resumeToken?: string): void;
|
|
69
83
|
snapshot(): Readonly<Record<string, string>>;
|
|
70
84
|
restore(tokens: Readonly<Record<string, string>>): void;
|
|
71
85
|
}
|
|
@@ -103,6 +117,30 @@ interface PlaybookPendingCall {
|
|
|
103
117
|
childSessionId: string;
|
|
104
118
|
}
|
|
105
119
|
|
|
120
|
+
interface PlaybookSuspendedCall extends PlaybookPendingCall {
|
|
121
|
+
stateId: string;
|
|
122
|
+
text: string;
|
|
123
|
+
turnId?: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface PlaybookRuntimeSnapshot {
|
|
127
|
+
schemaVersion: 3;
|
|
128
|
+
playbookId: string;
|
|
129
|
+
machine: JsonValue;
|
|
130
|
+
roleResumeTokens: { readonly [roleId: string]: string };
|
|
131
|
+
sequences: {
|
|
132
|
+
trace: number;
|
|
133
|
+
turn: number;
|
|
134
|
+
judgeCall: number;
|
|
135
|
+
playerCall: number;
|
|
136
|
+
playbookCall: number;
|
|
137
|
+
captainCall?: number;
|
|
138
|
+
};
|
|
139
|
+
state: PlaybookState;
|
|
140
|
+
pendingBossQuestions: readonly PlaybookPendingBossQuestion[];
|
|
141
|
+
suspendedCall?: PlaybookSuspendedCall;
|
|
142
|
+
}
|
|
143
|
+
|
|
106
144
|
type PlaybookRunResult =
|
|
107
145
|
| { outcome: 'quiescent' | 'no-action'; state: PlaybookState }
|
|
108
146
|
| {
|
|
@@ -151,7 +189,7 @@ exactly one `pendingCall` is active.
|
|
|
151
189
|
Control-plane exceptions reject the runtime method rather than masquerade as a
|
|
152
190
|
recoverable workflow `failed` result.
|
|
153
191
|
|
|
154
|
-
`PlaybookRuntimeOptions` is host-agnostic and carries only _per-run_ knobs
|
|
192
|
+
`PlaybookRuntimeOptions` is host-agnostic and carries only _per-run_ workflow knobs, 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.
|
|
155
193
|
The link compiler emits a typed options interface per playbook based on the FSM's `CodingInput` (or equivalent).
|
|
156
194
|
The CLI's absence of `--link-option` values does not mean that
|
|
157
195
|
`PlaybookRuntimeOptions` is empty. CLI link options are compile-time inputs;
|
|
@@ -162,15 +200,16 @@ remain a required readonly runtime option passed through to machine input; the
|
|
|
162
200
|
linker shall neither invent an empty catalog nor require it to be baked into a
|
|
163
201
|
CLI link option.
|
|
164
202
|
|
|
165
|
-
|
|
166
|
-
|
|
203
|
+
Concrete player binding and prompt identity are host policy and shall not enter `PlaybookRuntimeOptions`, machine input, or the emitted artifact.
|
|
204
|
+
For a shell-hosted runtime, `PlaybookSession.roleBindings` shall carry exactly the runtime's local roles, map each to its resolved player id and current prompt identity, and be the sole source for call targeting, player-facing prompt identity, concurrency keys, and trace player ids.
|
|
205
|
+
The host shall derive `promptIdentity` from the current effective model when present and the established adapter otherwise; a standalone runtime may omit the map and retain only its local role identity.
|
|
167
206
|
|
|
168
207
|
## PlaybookPorts contract
|
|
169
208
|
|
|
170
209
|
```typescript
|
|
171
210
|
interface PlaybookPorts {
|
|
172
211
|
callPlayer(
|
|
173
|
-
|
|
212
|
+
roleId: string,
|
|
174
213
|
prompt: string,
|
|
175
214
|
signal: AbortSignal,
|
|
176
215
|
options: PlayerCallOptions,
|
|
@@ -289,7 +328,7 @@ emission drain. Absent such a control-plane failure, if the combined signal
|
|
|
289
328
|
has aborted, ordinary abort settlement remains authoritative after the actor
|
|
290
329
|
reaches its error path.
|
|
291
330
|
|
|
292
|
-
Every linked runtime owns a map
|
|
331
|
+
Every standalone linked runtime owns a private continuation map keyed by resolved player id when `roleBindings` is supplied and by local role id otherwise.
|
|
293
332
|
Before reading a resolved direct-Captain or delegated-player result, the
|
|
294
333
|
runtime shall validate, detach, and freeze it through the shared
|
|
295
334
|
`validateCaptainResult` or `validatePlayerResult` helper. The accepted object
|
|
@@ -297,17 +336,18 @@ shape is exact: only the declared status and optional string fields are
|
|
|
297
336
|
allowed, JSON-unsafe members reject, and caller mutation after resolution
|
|
298
337
|
cannot change trace evidence or player continuity. Validation happens before
|
|
299
338
|
adopting a resume token or reading final text.
|
|
300
|
-
The first call to each
|
|
301
|
-
After a resolved call, the runtime shall replace the token when the result carries one
|
|
339
|
+
The first call to each private continuation key in a standalone playbook session shall pass `{ resume: false }`; later calls shall pass the exact stored token, so two sequential roles explicitly bound to one player id share one token even without a supplied `PlayerSessionStore`.
|
|
340
|
+
After a validated resolved call, the runtime shall replace the token when the result carries one, clear it only for an `ok` result that omits one, and preserve it for an `aborted` or `error` result that omits one; a rejected call with no result likewise leaves the prior token unchanged.
|
|
302
341
|
After awaiting a host Captain or player promise, the runtime shall re-check the
|
|
303
342
|
combined invocation/public-boundary signal before validating the result,
|
|
304
343
|
adopting a resume token, or emitting a successful finish. A host promise that
|
|
305
344
|
ignores cancellation and resolves late shall be paired as aborted and shall
|
|
306
345
|
not mutate continuity or masquerade as success.
|
|
307
346
|
The map survives actor reconstruction inside the same runtime and is discarded at `dispose`.
|
|
308
|
-
The runtime shall keep an in-flight set keyed by resolved player id
|
|
309
|
-
|
|
310
|
-
to
|
|
347
|
+
The runtime shall keep an in-flight set keyed by resolved player id when the
|
|
348
|
+
host supplies binding metadata, otherwise by local role id, and reject a
|
|
349
|
+
second concurrent call to the same key before crossing the host port.
|
|
350
|
+
Calls to distinct keys may overlap.
|
|
311
351
|
|
|
312
352
|
`callJudge` returns free-form text.
|
|
313
353
|
The runtime parses it per the state's adjudication strategy (§Captain adjudication).
|
|
@@ -346,7 +386,7 @@ The runtime never speaks to LLMs directly and never touches host types beyond `P
|
|
|
346
386
|
## Playbook trace
|
|
347
387
|
|
|
348
388
|
Every linked runtime shall emit a boundary-complete, ordered trace through `emitTelemetry` topic `playbook.trace`.
|
|
349
|
-
Each payload shall carry `schemaVersion:
|
|
389
|
+
Each payload shall carry `schemaVersion: 3`, the immutable session identity and
|
|
350
390
|
causality, a contiguous one-based `sequence`, a Unix-millisecond `timestamp`, a
|
|
351
391
|
trace `type`, event `payload`, and the runtime-local `turnId` / paired `callId`
|
|
352
392
|
where applicable.
|
|
@@ -371,7 +411,7 @@ type PlaybookTraceType =
|
|
|
371
411
|
| 'session.disposed';
|
|
372
412
|
|
|
373
413
|
interface PlaybookTraceEvent {
|
|
374
|
-
schemaVersion:
|
|
414
|
+
schemaVersion: 3;
|
|
375
415
|
sessionId: string;
|
|
376
416
|
playbookId: string;
|
|
377
417
|
rootSessionId: string;
|
|
@@ -387,7 +427,7 @@ interface PlaybookTraceEvent {
|
|
|
387
427
|
}
|
|
388
428
|
```
|
|
389
429
|
|
|
390
|
-
A composing host may supply `playerSessions` as a frame-local view of player continuation owned by the
|
|
430
|
+
A composing host may supply `playerSessions` as a frame-local view of player continuation owned by the logical Captain session.
|
|
391
431
|
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
432
|
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
433
|
A host that omits the view retains the runtime's private per-session continuation behavior.
|
|
@@ -401,7 +441,7 @@ The trace types are `session.started`, `boss.input.received`,
|
|
|
401
441
|
`boss.input.settled`, and `session.disposed`.
|
|
402
442
|
Call pairs carry exact prompts and replies, normalized failures, actor and state
|
|
403
443
|
identity, and their boundary-specific options.
|
|
404
|
-
`apply.started` and `apply.finished` are the paired
|
|
444
|
+
`apply.started` and `apply.finished` are the paired apply boundary of a
|
|
405
445
|
executed `apply()` call on a runtime implementing the optional control surface
|
|
406
446
|
(§Control surface): both carry the action id and idempotency `key` (plus the
|
|
407
447
|
singular `stateId` on start when one exists), the pair shares one
|
|
@@ -423,7 +463,8 @@ described top-level `state` and its singular `stateId` when present, as well as
|
|
|
423
463
|
its message and optional data; consumers shall not have to recover state
|
|
424
464
|
identity from a nested ad hoc object.
|
|
425
465
|
Judge results use `reply`; player start and finish payloads both carry the
|
|
426
|
-
|
|
466
|
+
local `roleId`, the resolved `playerId` when host binding metadata is
|
|
467
|
+
available, and the selected `resume`; Captain start and finish payloads both carry
|
|
427
468
|
the exact composed prompt, the boundary's selected `visibility`, the direct
|
|
428
469
|
invocation's `stateId` and `sourceItem`, and no player resume selection or
|
|
429
470
|
resume token — a visible workflow call carries its runtime-owned
|
|
@@ -498,51 +539,36 @@ Trace payloads never become Boss-visible status or prompt text.
|
|
|
498
539
|
The link compiler shall accept:
|
|
499
540
|
|
|
500
541
|
- The FSM artifact (path to a `.fsm.ts`).
|
|
501
|
-
- A **player binding** mapping GEARS players (declared in the
|
|
502
|
-
[text2gears](text2gears.md#players) source) to opaque player-identifier
|
|
503
|
-
strings.
|
|
504
|
-
Where no binding is supplied, the linker shall apply the default
|
|
505
|
-
binding — each player to its lowercased name (e.g. `Coder` → `coder`)
|
|
506
|
-
— and record the applied binding in the emitted header.
|
|
507
542
|
- An **adjudication strategy** (default: LLM-judge per state) and a
|
|
508
543
|
**Boss-event mapping** (default: free-text judge classification).
|
|
509
544
|
Both strategies are host-agnostic.
|
|
510
545
|
|
|
511
546
|
The host's identity does not enter compilation; the linked module runs unchanged under any host that implements `PlaybookPorts`.
|
|
512
547
|
|
|
513
|
-
##
|
|
548
|
+
## Role identity
|
|
514
549
|
|
|
515
|
-
Each delegated GEARS state names exactly one player
|
|
516
|
-
(`
|
|
517
|
-
The
|
|
518
|
-
`PlaybookPorts.callPlayer(playerId, …)`.
|
|
519
|
-
The host adapter routes that opaque string to its concrete primitive.
|
|
550
|
+
Each delegated GEARS state names exactly one canonical local role id (`player` actor `invoke.input.role`).
|
|
551
|
+
The linker shall retain that id in `PlaybookPorts.callPlayer(roleId, …)` without selecting a concrete player.
|
|
552
|
+
The host shall bind that local role id explicitly when it constructs the runtime.
|
|
520
553
|
Every direct-Captain and delegated-player invocation shall also carry its
|
|
521
554
|
working leaf's explicit
|
|
522
555
|
`stateId`; a linked runtime shall use that field for call identity and shall
|
|
523
556
|
not infer one leaf from a structured root snapshot.
|
|
524
|
-
Direct `captain` actor states
|
|
525
|
-
|
|
526
|
-
`captain` for them.
|
|
527
|
-
|
|
528
|
-
For composite players declared with aliases (e.g., `Committer = Coder | Reviewer`), the linker shall resolve the alias **per source item**.
|
|
529
|
-
Resolution inspects the `PlayerInput` fields populated at that state:
|
|
530
|
-
|
|
531
|
-
- If only one `<playerName>Player` field is present, bind to that player.
|
|
532
|
-
- If multiple are present, prefer the first-listed alternative in the alias declaration order.
|
|
533
|
-
- If none are present, fall back to the alias's first alternative.
|
|
534
|
-
|
|
535
|
-
Resolution shall be deterministic and recorded in the emitted module so future maintainers can audit it without re-running the linker.
|
|
536
|
-
|
|
537
|
-
The linker shall not invent player identifiers beyond the recorded default
|
|
538
|
-
binding, and shall not silently collapse aliases at the FSM level — composite
|
|
539
|
-
players keep their `player: 'Committer'` value on `PlayerInput`; resolution
|
|
540
|
-
decides only the `callPlayer` invocation.
|
|
557
|
+
Direct `captain` actor states call `PlaybookPorts.callCaptain`; the linker shall not synthesize a local role or concrete player id named `captain` for them.
|
|
558
|
+
The linker shall reject an alias-shaped role declaration rather than choose a runtime identity.
|
|
541
559
|
|
|
542
560
|
## Player prompt composition
|
|
543
561
|
|
|
544
562
|
The runtime shall compose the actual player prompt from the state's
|
|
545
563
|
`PlayerInput`.
|
|
564
|
+
The shared-factory `composePlayerPrompt` seam shall receive an
|
|
565
|
+
invocation-scoped `promptIdentity(roleId)` lookup as its second argument.
|
|
566
|
+
The lookup shall return the current detached session binding's prompt identity,
|
|
567
|
+
or the canonical local role id when bindings are absent, and shall reject an
|
|
568
|
+
undeclared role.
|
|
569
|
+
It shall expose neither the resolved player id nor the binding map, and the
|
|
570
|
+
runtime shall not place the lookup or any value read through it in options,
|
|
571
|
+
machine input, FSM context, or a persisted snapshot.
|
|
546
572
|
`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
573
|
A leading `>` inside that body is authored quoted-context content and shall reach the player unchanged.
|
|
548
574
|
|
|
@@ -687,7 +713,7 @@ classification is recoverable control input, not a public boundary rejection.
|
|
|
687
713
|
If a recovered `BOSS_REPLY` names no question that is currently pending, it is
|
|
688
714
|
such a malformed classification: emit the one recovery status, send no event,
|
|
689
715
|
leave the actor unchanged, and return `no-action` after emissions drain.
|
|
690
|
-
Host-owned runtime options, player bindings, and enabled-playbook catalogs are
|
|
716
|
+
Host-owned runtime options, role-to-player bindings, and enabled-playbook catalogs are
|
|
691
717
|
not Boss-event payload. The classifier schema and parser shall not invite or
|
|
692
718
|
accept them, and classified prose shall never overwrite their machine context.
|
|
693
719
|
Every recovered classifier object shall have exactly `type` plus the declared
|
|
@@ -705,14 +731,14 @@ aborted while the classifier finish emission was pending, return and trace the
|
|
|
705
731
|
same structured `aborted` result against the unchanged actor.
|
|
706
732
|
When the FSM supports a Boss-reply suspension state, the prompt shall inspect
|
|
707
733
|
the actor snapshot context and include each exact pending Boss question,
|
|
708
|
-
question id, and
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
734
|
+
question id, and discriminated Captain-or-role asker so the judge can distinguish a reply from a fresh directive.
|
|
735
|
+
With one pending question, a classified `BOSS_REPLY` that omits its optional id shall be filled with that sole id.
|
|
736
|
+
With several pending questions, the classifier shall require a known id.
|
|
737
|
+
A reply shall re-enter only
|
|
712
738
|
its recorded resume state and preserve the original intent, plan, prior child
|
|
713
739
|
results, and Q+A continuation context.
|
|
714
|
-
The classifier-facing pending-question block contains only `questionId`,
|
|
715
|
-
|
|
740
|
+
The classifier-facing pending-question block contains only `questionId`, `asker`, and `question`.
|
|
741
|
+
Internal `resumeStateId`, source-item identity, and
|
|
716
742
|
other machine-routing fields remain authoritative in snapshot context and
|
|
717
743
|
shall not be serialized into the judge prompt.
|
|
718
744
|
The allowed fresh directives while parked include every applicable root entry
|
|
@@ -1191,23 +1217,20 @@ The `PlaybookRuntime` shall:
|
|
|
1191
1217
|
failure cannot skip the parent disposal boundary or leave the runtime bound.
|
|
1192
1218
|
|
|
1193
1219
|
The actor's `lastError` field shall be surfaced via `emitStatus` when the machine enters its `failed` state.
|
|
1194
|
-
Presence of linker-emitted `
|
|
1195
|
-
|
|
1220
|
+
Presence of linker-emitted `roleStates` selects the canonical factory-backed status profile.
|
|
1221
|
+
That profile shall emit the selected Boss event type
|
|
1196
1222
|
before sending that event, exactly `→ <guard>` (with no payload-count or tally
|
|
1197
1223
|
rider) when a settling actor output carries a guard, and
|
|
1198
|
-
`⤷ <
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
that invoke the typed `player` actor; each value carries the exact
|
|
1202
|
-
that state's source-derived `meta.playbook.
|
|
1203
|
-
|
|
1204
|
-
entry, a non-player state, or a
|
|
1205
|
-
|
|
1206
|
-
shall
|
|
1207
|
-
|
|
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.
|
|
1224
|
+
`⤷ <Role>: <label>` only when the entered state appears in the linked module's `roleStates` metadata.
|
|
1225
|
+
It shall emit no raw state-id fallback for any other state.
|
|
1226
|
+
`roleStates` shall be a complete map of the FSM states
|
|
1227
|
+
that invoke the typed `player` actor; each schema-2 value carries the exact
|
|
1228
|
+
local role from that state's source-derived `meta.playbook.role` and the state's exact FSM description as `{ role, label }`.
|
|
1229
|
+
The factory shall reject an
|
|
1230
|
+
incomplete entry, a non-player state, or a role or label that differs from the FSM metadata.
|
|
1231
|
+
Artifact schema `1` and a missing compatibility declaration
|
|
1232
|
+
shall reject before interpretation because their legacy `player` values may
|
|
1233
|
+
encode bindings or aliases rather than canonical local roles.
|
|
1211
1234
|
For the default Captain runtime, an initial `ready` state and a terminal `done`
|
|
1212
1235
|
state shall not emit human status. The terminal response is already visible
|
|
1213
1236
|
Captain prose; a synthetic “entered done” message would present it twice.
|
|
@@ -1245,22 +1268,22 @@ quiescent state with actor status `active`.
|
|
|
1245
1268
|
At a safe capture point it shall return a JSON-safe
|
|
1246
1269
|
`PlaybookRuntimeSnapshot` carrying:
|
|
1247
1270
|
|
|
1248
|
-
- `schemaVersion`: literal `
|
|
1271
|
+
- `schemaVersion`: literal `3`.
|
|
1249
1272
|
- `playbookId`: the bound session's playbook id.
|
|
1250
1273
|
- `machine`: the root actor's `getPersistedSnapshot()` result, passed
|
|
1251
1274
|
through the shared JSON detachment with any raw `Error` context value
|
|
1252
1275
|
(for example FSM `lastError`) normalized to `{ name, message, stack? }`
|
|
1253
1276
|
first. The value is opaque to hosts.
|
|
1254
|
-
- `
|
|
1277
|
+
- `roleResumeTokens`: the local-role resume-token projection as a plain object
|
|
1255
1278
|
(§PlaybookPorts contract).
|
|
1256
1279
|
- `sequences`: the live `trace`, `turn`, `judgeCall`, `playerCall`, and
|
|
1257
1280
|
`playbookCall` counters, plus `captainCall` when the runtime supports direct
|
|
1258
|
-
Captain calls.
|
|
1259
|
-
|
|
1260
|
-
input, where restore uses `trace` as its collision-safe floor.
|
|
1281
|
+
Captain calls.
|
|
1282
|
+
A direct-Captain-capable runtime shall persist it in every schema-version-3 export.
|
|
1261
1283
|
- `state`: the current normalized state descriptor.
|
|
1262
1284
|
- `pendingBossQuestions`: the pending Boss question(s) from FSM context as
|
|
1263
|
-
a list of `{ questionId,
|
|
1285
|
+
a list of `{ questionId, asker, question, sourceItem? }`, where `asker` is
|
|
1286
|
+
`{ kind: 'captain' }` or `{ kind: 'role', roleId }`, empty when the
|
|
1264
1287
|
parked state awaits no reply. This list exists so hosts can surface the
|
|
1265
1288
|
question without parsing status lines or telemetry.
|
|
1266
1289
|
- `suspendedCall`: omitted when no nested call is pending; otherwise the
|
|
@@ -1278,19 +1301,16 @@ unsafe and returns `undefined`.
|
|
|
1278
1301
|
`restore(session, snapshot)` is an alternative to `init` under the same
|
|
1279
1302
|
lifecycle guards (§Session lifecycle): it shall reject when already
|
|
1280
1303
|
initialized, disposing, or disposed, and shall validate
|
|
1281
|
-
schema version `
|
|
1282
|
-
`
|
|
1283
|
-
descriptor-free legacy input; schema version `2` may carry the suspended-call
|
|
1284
|
-
descriptor above.
|
|
1304
|
+
schema version `3` and that `snapshot.playbookId` equals `session.playbookId` before touching state.
|
|
1305
|
+
Runtime snapshot schemas `1` and `2` shall reject before state binding because their token and pending-question fields conflate local roles, concrete players, and Captain identity.
|
|
1285
1306
|
The host supplies the same immutable `PlaybookSession` identity the
|
|
1286
1307
|
snapshot was exported under and recreates the runtime through the same
|
|
1287
1308
|
factory with equivalent options; the runtime does not diff options, and
|
|
1288
1309
|
module identity — that the factory constructing this runtime still
|
|
1289
1310
|
belongs to the snapshot's playbook — is likewise the host's check to
|
|
1290
1311
|
make before calling `restore`.
|
|
1291
|
-
`restore` shall bind the session, restore the
|
|
1292
|
-
sequence counters
|
|
1293
|
-
collision-safe floor for an absent legacy `captainCall`), and the
|
|
1312
|
+
`restore` shall bind the session and its current detached role bindings, restore the local-role token projection, the
|
|
1313
|
+
sequence counters, and the
|
|
1294
1314
|
prior-state descriptor from the snapshot,
|
|
1295
1315
|
prepare the shared nested bridge with the suspended-call descriptor or its
|
|
1296
1316
|
explicit absence, restore a descriptor's call-to-turn map entry, construct
|
|
@@ -1599,11 +1619,10 @@ The thin emitted module:
|
|
|
1599
1619
|
under TypeScript erasure; `placeholderFields` only for authored token/field
|
|
1600
1620
|
exceptions not covered by the canonical kebab-token-to-camel-field mapping
|
|
1601
1621
|
and the canonical `<#>` → `irNumber` special case; the
|
|
1602
|
-
transition-event payload fields the FSM's Boss union declares;
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
source-derived `meta.playbook.player` (an empty map when there is no such
|
|
1622
|
+
transition-event payload fields the FSM's Boss union declares; the
|
|
1623
|
+
complete `roleStates` status map derived from every FSM state that invokes
|
|
1624
|
+
the typed `player` actor, with each `role` copied from that state's
|
|
1625
|
+
source-derived `meta.playbook.role` (an empty map when there is no such
|
|
1607
1626
|
state); the
|
|
1608
1627
|
`verbatimPayloadFields` set derived from annotated result fields above; the
|
|
1609
1628
|
`controlContextFields` projection of §Control surface; and any
|
|
@@ -1641,13 +1660,13 @@ The thin emitted module:
|
|
|
1641
1660
|
fields?: Readonly<Record<string, XStateBossEventFieldSpec>>;
|
|
1642
1661
|
}
|
|
1643
1662
|
|
|
1644
|
-
interface
|
|
1645
|
-
|
|
1663
|
+
interface XStateRoleStateStatus {
|
|
1664
|
+
role: string;
|
|
1646
1665
|
label: string;
|
|
1647
1666
|
}
|
|
1648
1667
|
|
|
1649
1668
|
bossEvents?: readonly XStateBossEventSpec[];
|
|
1650
|
-
|
|
1669
|
+
roleStates?: Readonly<Record<string, XStateRoleStateStatus>>;
|
|
1651
1670
|
placeholderFields?: Readonly<Record<string, string>>;
|
|
1652
1671
|
```
|
|
1653
1672
|
|
|
@@ -1663,8 +1682,8 @@ The thin emitted module:
|
|
|
1663
1682
|
runtime-owned arm to have lost payload detail under erasure shall report
|
|
1664
1683
|
that gap rather than emit the entry.
|
|
1665
1684
|
- Supplies `spec.compat` with the compatibility values current at link time:
|
|
1666
|
-
`{ artifactSchema, runtimeAbi }`, where `artifactSchema` is `
|
|
1667
|
-
schema number of the thin-module format this §Output defines — and
|
|
1685
|
+
`{ artifactSchema, runtimeAbi }`, where `artifactSchema` is `2` — the
|
|
1686
|
+
schema number of the local-role thin-module format this §Output defines — and
|
|
1668
1687
|
`runtimeAbi` is the installed shared engine's `RUNTIME_ABI` self-report.
|
|
1669
1688
|
The linker shall verify that the installed engine lists the emitted
|
|
1670
1689
|
schema in `SUPPORTED_ARTIFACT_SCHEMAS` and treat its absence as a
|
|
@@ -1675,7 +1694,8 @@ The thin emitted module:
|
|
|
1675
1694
|
declaration against the engine instance that actually loads the emitted
|
|
1676
1695
|
module and fails construction on a mismatch, so an artifact linked under
|
|
1677
1696
|
one engine cannot run silently skewed under another. Modules emitted
|
|
1678
|
-
before this contract carry no `compat` member and
|
|
1697
|
+
before this contract carry no `compat` member and shall reject before interpretation.
|
|
1698
|
+
- Requires the containing public registry manifest to advertise the same `artifactSchema: 2`; the Captain host shall reject a missing or disagreeing registry value before constructing this runtime, and a bespoke runtime profile shall advertise the same schema without claiming this shared factory's `runtimeAbi`.
|
|
1679
1699
|
- Default-exports the factory call as `createPlaybookRuntime`, typed
|
|
1680
1700
|
`PlaybookRuntimeFactory<PlaybookRuntimeOptions>`.
|
|
1681
1701
|
- Exposes, under an `_internal` export, the pure helpers verification
|
|
@@ -1693,7 +1713,7 @@ The thin emitted module:
|
|
|
1693
1713
|
speaks only `PlaybookPorts` for every agent and host concern; the
|
|
1694
1714
|
`node:child_process` dependency of §Script execution lives in the shared
|
|
1695
1715
|
factory, not in the emitted module.
|
|
1696
|
-
- Records the linker inputs (FSM path
|
|
1716
|
+
- Records the linker inputs (FSM path and strategies) in a
|
|
1697
1717
|
top-of-file header comment so the file is reproducible from the same
|
|
1698
1718
|
inputs.
|
|
1699
1719
|
- Sources the contract types (`PlayerResult`, `PlayerCallOptions`,
|
package/slc/text2gears.md
CHANGED
|
@@ -18,24 +18,24 @@ The second phase (spec items → state machine) is out of scope.
|
|
|
18
18
|
| source | text | .md |
|
|
19
19
|
| target | gears | .md |
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Roles
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Roles name playbook-local delegated work functions.
|
|
24
24
|
|
|
25
|
-
Two
|
|
25
|
+
Two fixed actors remain outside the role list:
|
|
26
26
|
|
|
27
27
|
- Boss: the human user
|
|
28
28
|
- Captain: the coordinating agent
|
|
29
29
|
|
|
30
|
-
Source may declare
|
|
31
|
-
|
|
30
|
+
Source may declare delegated roles in an opening `Roles:` section.
|
|
31
|
+
Each role shall be unique and shall not alias another role; concrete player selection and sharing belong to explicit host configuration.
|
|
32
|
+
Role names shall also be unique after canonical lowercase-id derivation, so declarations such as `Coder` and `coder` reject rather than collapse to one manifest role.
|
|
32
33
|
E.g.:
|
|
33
34
|
|
|
34
35
|
- Coder
|
|
35
36
|
- Reviewer
|
|
36
|
-
- Committer = Coder | Reviewer
|
|
37
37
|
|
|
38
|
-
Capitalize English
|
|
38
|
+
Capitalize English role names (e.g., `Writer`); quote non-English names (e.g., `作者`) when needed to distinguish from prose.
|
|
39
39
|
|
|
40
40
|
## Behaviors
|
|
41
41
|
|
|
@@ -47,15 +47,15 @@ will not be visible to downstream compilers or verification.
|
|
|
47
47
|
The behavior kind shall be one of:
|
|
48
48
|
|
|
49
49
|
- direct Captain work, written `Captain shall <behavior>:` without naming a
|
|
50
|
-
delegated
|
|
51
|
-
- delegated
|
|
52
|
-
existing `Captain shall relay ... to <
|
|
50
|
+
delegated role;
|
|
51
|
+
- delegated-role work, written `Captain shall prompt <Role>:` or the
|
|
52
|
+
existing `Captain shall relay ... to <Role> ...:` form; or
|
|
53
53
|
- a literal or dynamic nested playbook call as defined below.
|
|
54
54
|
|
|
55
55
|
Direct Captain work means the coordinating Captain performs the behavior
|
|
56
56
|
itself. It shall not be rewritten as `Captain shall prompt Captain`, because
|
|
57
|
-
Captain is a distinct runtime actor rather than a
|
|
58
|
-
Delegated work shall name the declared
|
|
57
|
+
Captain is a distinct runtime actor rather than a role binding.
|
|
58
|
+
Delegated work shall name the declared role that receives the prompt.
|
|
59
59
|
Prompts shall be blockquoted, one point per line.
|
|
60
60
|
When Source already supplies the complete blockquoted acting prompt for a
|
|
61
61
|
behavior, text2gears shall preserve those prompt lines exactly (apart from the
|
|
@@ -210,10 +210,11 @@ heading.
|
|
|
210
210
|
Every item in one parallel group shall receive the same completed-prior-group
|
|
211
211
|
inputs; no item prompt may depend on another member's result from the current
|
|
212
212
|
group.
|
|
213
|
-
Every member shall delegate to a named
|
|
214
|
-
|
|
213
|
+
Every member shall delegate to a distinct named role; a group that repeats one canonical role is malformed because one role resolves to one player.
|
|
214
|
+
Direct-Captain work shares one
|
|
215
215
|
Captain session and nested calls share one pending-child stack slot, so neither
|
|
216
|
-
kind may receive parallel-group metadata.
|
|
216
|
+
kind may receive parallel-group metadata.
|
|
217
|
+
If Source explicitly requires either
|
|
217
218
|
unsupported kind to run concurrently, text2gears shall report that the source
|
|
218
219
|
cannot be represented rather than silently serialize it or emit metadata the
|
|
219
220
|
next phase cannot compile.
|
|
@@ -311,15 +312,15 @@ prose, acting prompts, and result descriptions follow the Source language,
|
|
|
311
312
|
read per the matching localization of the GEARS definition [[1]].
|
|
312
313
|
The four `Captain shall` acting-clause forms defined above (direct,
|
|
313
314
|
delegated, nested playbook call, and script), guard names, and the
|
|
314
|
-
`
|
|
315
|
+
`Roles:` and `Results:` labels are fixed machine syntax and stay in this
|
|
315
316
|
exact English form regardless of Source language.
|
|
316
317
|
|
|
317
318
|
## Transformation-spec sources
|
|
318
319
|
|
|
319
320
|
A Source may itself be the normative specification of a transformation — e.g., a compiler phase definition, as when a meta pipeline compiles this file.
|
|
320
|
-
Such a Source declares no
|
|
321
|
+
Such a Source declares no roles and prompts none; its implied procedure is that Captain performs the specified transformation on request.
|
|
321
322
|
Compose Captain-acting spec items for it: when a transformation request names the specification's source and target, Captain shall carry out the transformation as specified.
|
|
322
|
-
Prompts shall carry the specification's normative requirements as instructions to Captain — deduplicated, one point per line — without inventing
|
|
323
|
+
Prompts shall carry the specification's normative requirements as instructions to Captain — deduplicated, one point per line — without inventing roles, triggers, or requirements the specification does not state.
|
|
323
324
|
|
|
324
325
|
## Composition
|
|
325
326
|
|
package/src/runtime.d.ts
CHANGED
|
@@ -8,11 +8,15 @@ export interface PlayerCallOptions {
|
|
|
8
8
|
resume: string | false;
|
|
9
9
|
}
|
|
10
10
|
export interface PlayerSessionStore {
|
|
11
|
-
select(
|
|
12
|
-
update(
|
|
11
|
+
select(roleId: string): string | false;
|
|
12
|
+
update(roleId: string, resumeToken?: string): void;
|
|
13
13
|
snapshot(): Readonly<Record<string, string>>;
|
|
14
14
|
restore(tokens: Readonly<Record<string, string>>): void;
|
|
15
15
|
}
|
|
16
|
+
export interface PlaybookRoleBinding {
|
|
17
|
+
readonly playerId: string;
|
|
18
|
+
readonly promptIdentity: string;
|
|
19
|
+
}
|
|
16
20
|
export interface CaptainCallOptions {
|
|
17
21
|
visibility: 'visible' | 'hidden';
|
|
18
22
|
resume: string | false;
|
|
@@ -100,7 +104,7 @@ export type PlaybookRunResult = {
|
|
|
100
104
|
pendingCall: PlaybookPendingCall;
|
|
101
105
|
};
|
|
102
106
|
export interface PlaybookPorts {
|
|
103
|
-
callPlayer(
|
|
107
|
+
callPlayer(roleId: string, prompt: string, signal: AbortSignal, options: PlayerCallOptions): Promise<PlayerResult>;
|
|
104
108
|
callCaptain(prompt: string, signal: AbortSignal, options: CaptainCallOptions): Promise<CaptainResult>;
|
|
105
109
|
callJudge(prompt: string, signal: AbortSignal): Promise<string>;
|
|
106
110
|
callPlaybook(request: PlaybookCallRequest, signal: AbortSignal): Promise<PlaybookCallStart>;
|
|
@@ -117,12 +121,13 @@ export interface PlaybookSession {
|
|
|
117
121
|
parentSessionId?: string;
|
|
118
122
|
parentCallId?: string;
|
|
119
123
|
depth: number;
|
|
124
|
+
roleBindings?: Readonly<Record<string, PlaybookRoleBinding>>;
|
|
120
125
|
playerSessions?: PlayerSessionStore;
|
|
121
126
|
ports: PlaybookPorts;
|
|
122
127
|
}
|
|
123
128
|
export type PlaybookTraceType = 'session.started' | 'boss.input.received' | 'judge.call.started' | 'judge.call.finished' | 'player.call.started' | 'player.call.finished' | 'captain.call.started' | 'captain.call.finished' | 'playbook.call.started' | 'playbook.call.finished' | 'apply.started' | 'apply.finished' | 'fsm.transition' | 'status.emitted' | 'boss.input.settled' | 'session.disposed';
|
|
124
129
|
export interface PlaybookTraceEvent {
|
|
125
|
-
schemaVersion:
|
|
130
|
+
schemaVersion: 3;
|
|
126
131
|
sessionId: string;
|
|
127
132
|
playbookId: string;
|
|
128
133
|
rootSessionId: string;
|
|
@@ -138,15 +143,21 @@ export interface PlaybookTraceEvent {
|
|
|
138
143
|
}
|
|
139
144
|
export interface PlaybookPendingBossQuestion {
|
|
140
145
|
questionId: string;
|
|
141
|
-
|
|
146
|
+
asker: {
|
|
147
|
+
kind: 'captain';
|
|
148
|
+
} | {
|
|
149
|
+
kind: 'role';
|
|
150
|
+
roleId: string;
|
|
151
|
+
};
|
|
142
152
|
question: string;
|
|
143
153
|
sourceItem?: string;
|
|
144
154
|
}
|
|
145
|
-
interface
|
|
155
|
+
export interface PlaybookRuntimeSnapshot {
|
|
156
|
+
schemaVersion: 3;
|
|
146
157
|
playbookId: string;
|
|
147
158
|
machine: JsonValue;
|
|
148
|
-
|
|
149
|
-
readonly [
|
|
159
|
+
roleResumeTokens: {
|
|
160
|
+
readonly [roleId: string]: string;
|
|
150
161
|
};
|
|
151
162
|
sequences: {
|
|
152
163
|
trace: number;
|
|
@@ -158,14 +169,8 @@ interface PlaybookRuntimeSnapshotFields {
|
|
|
158
169
|
};
|
|
159
170
|
state: PlaybookState;
|
|
160
171
|
pendingBossQuestions: readonly PlaybookPendingBossQuestion[];
|
|
161
|
-
}
|
|
162
|
-
export type PlaybookRuntimeSnapshot = PlaybookRuntimeSnapshotFields & ({
|
|
163
|
-
schemaVersion: 1;
|
|
164
|
-
suspendedCall?: never;
|
|
165
|
-
} | {
|
|
166
|
-
schemaVersion: 2;
|
|
167
172
|
suspendedCall?: PlaybookSuspendedCall;
|
|
168
|
-
}
|
|
173
|
+
}
|
|
169
174
|
export interface PlaybookControlAction {
|
|
170
175
|
id: string;
|
|
171
176
|
label: string;
|
|
@@ -210,4 +215,3 @@ export interface PlaybookRuntime {
|
|
|
210
215
|
dispose(): Promise<void>;
|
|
211
216
|
}
|
|
212
217
|
export type PlaybookRuntimeFactory<Options = unknown> = (options: Options) => PlaybookRuntime;
|
|
213
|
-
export {};
|