@sublang/playbook 5.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +22 -14
  2. package/docs/cli.md +138 -90
  3. package/docs/configuration.md +113 -29
  4. package/docs/embedding.md +24 -16
  5. package/package.json +42 -21
  6. package/reference/sdlc/captain.playbook/captain.playbook.js +2 -0
  7. package/reference/sdlc/captain.playbook/captain.playbook.ts +2 -0
  8. package/reference/sdlc/code.md +55 -97
  9. package/reference/sdlc/code.playbook/bin/launch-config.js +938 -0
  10. package/reference/sdlc/code.playbook/bin/playbook.js +145 -562
  11. package/reference/sdlc/code.playbook/bin/provision.js +84 -38
  12. package/reference/sdlc/code.playbook/bin/run.js +1171 -983
  13. package/reference/sdlc/code.playbook/bin/session-store.js +1169 -0
  14. package/reference/sdlc/code.playbook/code.fsm.d.ts +229 -94
  15. package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +26 -44
  16. package/reference/sdlc/code.playbook/code.fsm.introspect.js +61 -66
  17. package/reference/sdlc/code.playbook/code.fsm.introspect.ts +100 -149
  18. package/reference/sdlc/code.playbook/code.fsm.js +587 -1347
  19. package/reference/sdlc/code.playbook/code.fsm.ts +809 -1650
  20. package/reference/sdlc/code.playbook/code.gears.md +51 -263
  21. package/reference/sdlc/code.playbook/code.playbook.d.ts +8 -47
  22. package/reference/sdlc/code.playbook/code.playbook.js +69 -656
  23. package/reference/sdlc/code.playbook/code.playbook.ts +90 -867
  24. package/reference/sdlc/code.playbook/code.registry.d.ts +9 -25
  25. package/reference/sdlc/code.playbook/code.registry.js +20 -78
  26. package/reference/sdlc/code.playbook/code.registry.ts +58 -122
  27. package/reference/sdlc/code.playbook/playbook-captain.d.ts +70 -3
  28. package/reference/sdlc/code.playbook/playbook-captain.js +954 -80
  29. package/reference/sdlc/code.playbook/playbook-captain.ts +1408 -80
  30. package/reference/sdlc/code.playbook/playbook.config.template.yaml +37 -36
  31. package/reference/sdlc/decide.md +54 -0
  32. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +261 -0
  33. package/reference/sdlc/decide.playbook/decide.fsm.js +894 -0
  34. package/reference/sdlc/decide.playbook/decide.fsm.ts +1152 -0
  35. package/reference/sdlc/decide.playbook/decide.gears.md +88 -0
  36. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +67 -0
  37. package/reference/sdlc/{discuss.playbook/discuss.playbook.js → decide.playbook/decide.playbook.js} +545 -372
  38. package/reference/sdlc/{discuss.playbook/discuss.playbook.ts → decide.playbook/decide.playbook.ts} +665 -454
  39. package/reference/sdlc/decide.playbook/decide.registry.d.ts +41 -0
  40. package/reference/sdlc/decide.playbook/decide.registry.js +60 -0
  41. package/reference/sdlc/decide.playbook/decide.registry.ts +125 -0
  42. package/reference/sdlc/review.md +81 -0
  43. package/reference/sdlc/review.playbook/review.fsm.d.ts +183 -0
  44. package/reference/sdlc/review.playbook/review.fsm.js +524 -0
  45. package/reference/sdlc/review.playbook/review.fsm.ts +652 -0
  46. package/reference/sdlc/review.playbook/review.gears.md +112 -0
  47. package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -0
  48. package/reference/sdlc/review.playbook/review.playbook.js +112 -0
  49. package/reference/sdlc/review.playbook/review.playbook.ts +201 -0
  50. package/reference/sdlc/review.playbook/review.registry.d.ts +43 -0
  51. package/reference/sdlc/review.playbook/review.registry.js +73 -0
  52. package/reference/sdlc/review.playbook/review.registry.ts +138 -0
  53. package/slc/gears2fsm.md +13 -4
  54. package/slc/link.md +83 -14
  55. package/slc/text2gears.md +22 -2
  56. package/src/runtime.d.ts +21 -2
  57. package/src/runtime.ts +38 -6
  58. package/src/xstate-playbook-runtime.d.ts +9 -2
  59. package/src/xstate-playbook-runtime.js +319 -35
  60. package/src/xstate-playbook-runtime.ts +412 -41
  61. package/src/xstate-runtime.d.ts +19 -2
  62. package/src/xstate-runtime.js +384 -57
  63. package/src/xstate-runtime.ts +542 -71
  64. package/reference/sdlc/discuss.md +0 -93
  65. package/reference/sdlc/discuss.playbook/discuss.fsm.d.ts +0 -396
  66. package/reference/sdlc/discuss.playbook/discuss.fsm.js +0 -2067
  67. package/reference/sdlc/discuss.playbook/discuss.fsm.ts +0 -2465
  68. package/reference/sdlc/discuss.playbook/discuss.gears.md +0 -258
  69. package/reference/sdlc/discuss.playbook/discuss.playbook.d.ts +0 -113
  70. package/reference/sdlc/discuss.playbook/discuss.registry.d.ts +0 -58
  71. package/reference/sdlc/discuss.playbook/discuss.registry.js +0 -97
  72. 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
@@ -243,13 +244,15 @@ Each state shall declare:
243
244
  - a stable `id` (for `#id` targeting and Boss interrupts);
244
245
  - an intuitive state key (the property name under `states: { ... }`);
245
246
  - a one-line `description` (for inspector tools and documentation);
246
- - JSON-safe `meta: { playbook: { stateId, description } }` repeating its
247
- stable id and description so linked runtimes can discover active public
248
- 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`;
249
252
  - if it invokes the direct `captain` actor: `invoke.input` carrying
250
253
  `sourceItem`, `prompt`, and `result` (per [Setup](#setup));
251
254
  - if it invokes the delegated `player` actor: `invoke.input` additionally
252
- carrying `player`;
255
+ carrying the same source-derived `player` as `meta.playbook.player`;
253
256
  - if it invokes the `script` actor: `invoke.input` carrying `stateId`,
254
257
  `sourceItem`, `command`, and `result` (per [Setup](#setup)) — no `prompt`
255
258
  and no `player`.
@@ -598,6 +601,11 @@ context precondition required to enter that target safely. It shall not jump
598
601
  into a working or reassessment state with missing intent, prior result, plan,
599
602
  or other required context and shall not invent defaults merely to make an
600
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.
601
609
  XState automatically stops the current state's invoked actor on transition [[2]].
602
610
  Where the default Captain's routing state accepts a fresh intent while another
603
611
  state or Boss-reply wait is active, its `BOSS_INTERRUPT` event shall carry a
@@ -642,6 +650,7 @@ decision state would add a seventh outcome to a closed six-action contract
642
650
  whose guard discriminants [Setup](#setup) fixes, and is nonconformant.
643
651
  There is no source-level opt-in annotation and no `needsBossReply` result metadata in GEARS output.
644
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.
645
654
 
646
655
  For every captain- and player-invoking state outside a controller machine,
647
656
  the compiler shall add
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
@@ -379,6 +387,11 @@ interface PlaybookTraceEvent {
379
387
  }
380
388
  ```
381
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
+
382
395
  The trace types are `session.started`, `boss.input.received`,
383
396
  `judge.call.started`, `judge.call.finished`, `player.call.started`,
384
397
  `player.call.finished`, `captain.call.started`, `captain.call.finished`,
@@ -531,6 +544,7 @@ decides only the `callPlayer` invocation.
531
544
  The runtime shall compose the actual player prompt from the state's
532
545
  `PlayerInput`.
533
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.
534
548
 
535
549
  The composer may prepend structured labelled blocks from typed `PlayerInput`
536
550
  fields the FSM exposes (for example `Boss intent:`, `Review items:`,
@@ -731,6 +745,9 @@ clause (or equivalent typed output metadata). Backticked prose before that
731
745
  clause can name statuses, guards, or concepts such as `ok`, `aborted`, and
732
746
  `error`; those names are not output properties and shall never become required
733
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.
734
751
  For a direct Captain result, `question` and `response` are human-presentation
735
752
  fields owned by the visible call rather than fields authored by the hidden
736
753
  judge.
@@ -784,8 +801,8 @@ structured adjudication merely to present the same response.
784
801
  That visible-call presentation — visible Captain prose as the Boss
785
802
  presentation, a separate hidden adjudicator that never authors the
786
803
  `question`/`response` fields, and runtime injection of the visible
787
- `finalText` — stays scoped to visible-presentation playbooks such as CODE
788
- and DISCUSS. For a controller playbook, whose FSM declares the controller
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
789
806
  decision-state class of [gears2fsm "Setup"](gears2fsm.md#setup), the
790
807
  Captain-call presentation admits the hidden controller form instead
791
808
  (DR-029): the decision and closing-reply Captain calls run
@@ -1174,6 +1191,23 @@ The `PlaybookRuntime` shall:
1174
1191
  failure cannot skip the parent disposal boundary or leave the runtime bound.
1175
1192
 
1176
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.
1177
1211
  For the default Captain runtime, an initial `ready` state and a terminal `done`
1178
1212
  state shall not emit human status. The terminal response is already visible
1179
1213
  Captain prose; a synthetic “entered done” message would present it twice.
@@ -1206,12 +1240,12 @@ durability, the pair shall behave as follows.
1206
1240
 
1207
1241
  `exportSnapshot()` shall return `undefined` unless the runtime is at a safe
1208
1242
  capture point: initialized, not disposing or disposed, no active
1209
- `handleBossInput`/`resumePlaybookCall` boundary, no pending nested playbook
1210
- call, and the root actor at a quiescent state with actor status `active`.
1243
+ `handleBossInput`/`resumePlaybookCall` boundary, and the root actor at a
1244
+ quiescent state with actor status `active`.
1211
1245
  At a safe capture point it shall return a JSON-safe
1212
1246
  `PlaybookRuntimeSnapshot` carrying:
1213
1247
 
1214
- - `schemaVersion`: literal `1`.
1248
+ - `schemaVersion`: literal `2`.
1215
1249
  - `playbookId`: the bound session's playbook id.
1216
1250
  - `machine`: the root actor's `getPersistedSnapshot()` result, passed
1217
1251
  through the shared JSON detachment with any raw `Error` context value
@@ -1221,19 +1255,33 @@ At a safe capture point it shall return a JSON-safe
1221
1255
  (§PlaybookPorts contract).
1222
1256
  - `sequences`: the live `trace`, `turn`, `judgeCall`, `playerCall`, and
1223
1257
  `playbookCall` counters, plus `captainCall` when the runtime supports direct
1224
- Captain calls. `captainCall` remains optional under schema version `1` for
1225
- backward compatibility; a direct-Captain-capable runtime shall persist it.
1258
+ Captain calls. A direct-Captain-capable runtime shall persist it in current
1259
+ schema-version-2 exports; it remains optional in legacy schema-version-1
1260
+ input, where restore uses `trace` as its collision-safe floor.
1226
1261
  - `state`: the current normalized state descriptor.
1227
1262
  - `pendingBossQuestions`: the pending Boss question(s) from FSM context as
1228
1263
  a list of `{ questionId, player, question, sourceItem? }`, empty when the
1229
1264
  parked state awaits no reply. This list exists so hosts can surface the
1230
1265
  question without parsing status lines or telemetry.
1266
+ - `suspendedCall`: omitted when no nested call is pending; otherwise the
1267
+ shared nested bridge's complete `callId`, source `stateId`, target
1268
+ `playbookId`, exact handed-off `text`, and `childSessionId`, enriched with
1269
+ the call-to-turn map's optional `turnId`.
1270
+
1271
+ A pending nested call is exportable only when the bridge's pending identity
1272
+ and complete descriptor agree and the call-to-turn map owns that exact call
1273
+ id, including ownership whose value is absent. A bridge descriptor that
1274
+ already carries a turn id shall equal that map value. Any missing or
1275
+ inconsistent bridge, descriptor, or turn-ownership record makes the capture
1276
+ unsafe and returns `undefined`.
1231
1277
 
1232
1278
  `restore(session, snapshot)` is an alternative to `init` under the same
1233
1279
  lifecycle guards (§Session lifecycle): it shall reject when already
1234
1280
  initialized, disposing, or disposed, and shall validate
1235
- `snapshot.schemaVersion` and that `snapshot.playbookId` equals
1236
- `session.playbookId` before touching state.
1281
+ schema version `1` or `2` and that `snapshot.playbookId` equals
1282
+ `session.playbookId` before touching state. Schema version `1` remains a
1283
+ descriptor-free legacy input; schema version `2` may carry the suspended-call
1284
+ descriptor above.
1237
1285
  The host supplies the same immutable `PlaybookSession` identity the
1238
1286
  snapshot was exported under and recreates the runtime through the same
1239
1287
  factory with equivalent options; the runtime does not diff options, and
@@ -1244,13 +1292,22 @@ make before calling `restore`.
1244
1292
  sequence counters (using the persisted global `trace` counter as a
1245
1293
  collision-safe floor for an absent legacy `captainCall`), and the
1246
1294
  prior-state descriptor from the snapshot,
1247
- construct the actor with the persisted `machine` snapshot, and start it
1295
+ prepare the shared nested bridge with the suspended-call descriptor or its
1296
+ explicit absence, restore a descriptor's call-to-turn map entry, construct
1297
+ the actor with the persisted `machine` snapshot, and start it
1248
1298
  with root inspection emissions suppressed so rehydration emits no
1249
1299
  `session.started` trace, no transition trace, and no human status — the
1250
1300
  session already started, and the next public boundary continues the
1251
- contiguous trace sequence. After start, a restored actor whose status is
1252
- not `active` or whose state descriptor cannot be normalized shall fail
1253
- `restore` through the same failed-start cleanup path as `init`.
1301
+ contiguous trace sequence. After start, the runtime shall normalize the
1302
+ actual actor state with the prepared suspended call as its pending identity
1303
+ and require it to equal the detached persisted state exactly, including
1304
+ active status. It shall drain suppressed startup work and invoke the bridge's
1305
+ `confirmRestore` as the final fallible restore step, publishing the pending
1306
+ identity only after every other validation succeeds. A missing, extra, or
1307
+ mismatched reconstructed invocation, an actual/persisted state mismatch, or
1308
+ any other failed validation shall fail `restore` through the same
1309
+ failed-start cleanup path as `init`, rolling back provisional bridge and turn
1310
+ ownership without a child-host call or duplicate start/finish boundary.
1254
1311
  A restore failure shall leave the runtime unbound so `dispose` remains
1255
1312
  callable and terminal.
1256
1313
 
@@ -1406,7 +1463,7 @@ runtime's emission-failure channel to surface from the next public boundary
1406
1463
  that drains.
1407
1464
 
1408
1465
  The recorded receipts and the recorded last classified event are
1409
- process-local: the schema-1 parked-session snapshot persists neither, and a
1466
+ process-local: the durable runtime snapshot persists neither, and a
1410
1467
  restored runtime advertises a retry again only after its next classified
1411
1468
  event.
1412
1469
 
@@ -1544,6 +1601,11 @@ The thin emitted module:
1544
1601
  and the canonical `<#>` → `irNumber` special case; the
1545
1602
  transition-event payload fields the FSM's Boss union declares; a
1546
1603
  non-default player binding where the linker inputs supplied one; the
1604
+ complete `playerStates` status map derived from every FSM state that invokes
1605
+ the typed `player` actor, with each `player` copied from that state's
1606
+ source-derived `meta.playbook.player` (an empty map when there is no such
1607
+ state); the
1608
+ `verbatimPayloadFields` set derived from annotated result fields above; the
1547
1609
  `controlContextFields` projection of §Control surface; and any
1548
1610
  per-playbook strategy override (classifier, prompt composers,
1549
1611
  required-field extraction, status formatting) an earlier section of this
@@ -1579,7 +1641,13 @@ The thin emitted module:
1579
1641
  fields?: Readonly<Record<string, XStateBossEventFieldSpec>>;
1580
1642
  }
1581
1643
 
1644
+ interface XStatePlayerStateStatus {
1645
+ player: string;
1646
+ label: string;
1647
+ }
1648
+
1582
1649
  bossEvents?: readonly XStateBossEventSpec[];
1650
+ playerStates?: Readonly<Record<string, XStatePlayerStateStatus>>;
1583
1651
  placeholderFields?: Readonly<Record<string, string>>;
1584
1652
  ```
1585
1653
 
@@ -1629,6 +1697,7 @@ The thin emitted module:
1629
1697
  top-of-file header comment so the file is reproducible from the same
1630
1698
  inputs.
1631
1699
  - Sources the contract types (`PlayerResult`, `PlayerCallOptions`,
1700
+ `PlayerSessionStore`,
1632
1701
  `CaptainResult`, `CaptainCallOptions`, `PlaybookPorts`, `PlaybookSession`,
1633
1702
  `PlaybookTraceEvent`,
1634
1703
  `PlaybookCallRequest`, `PlaybookCallResult`, `PlaybookCallStart`,
package/slc/text2gears.md CHANGED
@@ -65,6 +65,20 @@ Those requirements remain in the item's condition or `Results:` metadata.
65
65
  Adding control-oriented prompt lines merely to restate them changes the
66
66
  Boss-visible contract and is nonconformant.
67
67
 
68
+ ### Authored prompt fragments
69
+
70
+ Source may compose one acting prompt from authored Markdown instruction blocks and runtime context that it explicitly says to relay in quotes (`>`).
71
+ A fenced `markdown` block introduced as an instruction or prompt is an authored static prompt fragment: its fence delimiters are Source syntax, while every interior line and blank line is prompt content preserved after documented Markdown unescaping.
72
+
73
+ An instruction fence and a relayed-context fragment that apply to one behavior shall appear in the target blockquote in their Source order.
74
+ Distinct non-empty fragments shall be separated by one blank prompt line unless Source explicitly supplies a different boundary.
75
+ text2gears shall not move a shared instruction ahead of behavior-specific context, move quoted evidence after an instruction that Source says follows the evidence, or otherwise regroup fragments for convenience.
76
+
77
+ Where Source says that a runtime value is relayed in quotes, the leading `>` is prompt content rather than Source-only blockquote syntax.
78
+ If Source supplies a blockquoted template for that relay, text2gears shall keep one literal leading `>` on every quoted line; the target GEARS line therefore uses its outer blockquote marker followed by the literal marker, such as `> > Coder output: <coder-output>`.
79
+ If Source names the relayed value but supplies no template, text2gears shall emit its canonical typed placeholder on a line beginning with literal `> ` and shall not summarize, paraphrase, or invent a value in its place.
80
+ An ordinary Source blockquote that specifies a complete acting prompt without requiring quoted relay retains the existing rule above: its one leading marker is Source syntax and is not prompt content.
81
+
68
82
  Source statements that assign active-leaf routing, call identity, suspension,
69
83
  or return matching to the host describe execution preconditions rather than
70
84
  behaviors for Captain to perform. text2gears shall use such a statement only as
@@ -139,6 +153,11 @@ A single-outcome producer then declares exactly one bullet naming the
139
153
  property; this consumed-output case is the sole one in which a
140
154
  single-outcome behavior carries a `Results:` label.
141
155
 
156
+ Where a later prompt relays a delegated player's whole final response as quoted context, the producer shall declare that property in the exact annotated form `` `<field>: <verbatim final text>` ``.
157
+ The annotation makes the field runtime-owned: the adjudicator selects the result guard, while the linked runtime carries the player's canonical final text into that field instead of asking a judge to reproduce it.
158
+ A distinct typed field extracted from that response remains judge-authored even when a later prompt quotes its exact value; quoting a field does not turn it into the player's whole final response.
159
+ One property name shall not be annotated as verbatim in one result contract and judge-authored in another; text2gears shall choose distinct properties or report that the Source cannot be represented by the current contract.
160
+
142
161
  Result metadata is compiler control data, not part of the acting agent's
143
162
  prompt.
144
163
  text2gears shall not put guard names, result-property schema, JSON control
@@ -202,11 +221,11 @@ next phase cannot compile.
202
221
  Example:
203
222
 
204
223
  ```markdown
205
- ### DISCUSS-1
224
+ ### DECIDE-1
206
225
 
207
226
  Parallel group: initial-proposals
208
227
 
209
- When Boss gives a topic, Captain shall prompt Host:
228
+ When Boss gives a topic, Captain shall prompt Coder:
210
229
 
211
230
  > Propose your design independently.
212
231
  ```
@@ -306,6 +325,7 @@ Prompts shall carry the specification's normative requirements as instructions t
306
325
 
307
326
  Source snippets may overlap or duplicate.
308
327
  When composing them into a spec item, text2gears shall deduplicate identical prompt lines.
328
+ It shall not deduplicate across distinct authored fragments when doing so would erase a fragment boundary or change the Source-ordered prompt.
309
329
 
310
330
  Each spec item addresses one state behavior and carries its full final prompt (the static part).
311
331
  Cross-item duplication is acceptable: spec items are compiled artifacts; Source is what users maintain.
package/src/runtime.d.ts CHANGED
@@ -7,6 +7,12 @@ export interface PlayerResult {
7
7
  export interface PlayerCallOptions {
8
8
  resume: string | false;
9
9
  }
10
+ export interface PlayerSessionStore {
11
+ select(playerId: string): string | false;
12
+ update(playerId: string, resumeToken?: string): void;
13
+ snapshot(): Readonly<Record<string, string>>;
14
+ restore(tokens: Readonly<Record<string, string>>): void;
15
+ }
10
16
  export interface CaptainCallOptions {
11
17
  visibility: 'visible' | 'hidden';
12
18
  resume: string | false;
@@ -41,6 +47,11 @@ export interface PlaybookPendingCall {
41
47
  playbookId: string;
42
48
  childSessionId: string;
43
49
  }
50
+ export interface PlaybookSuspendedCall extends PlaybookPendingCall {
51
+ stateId: string;
52
+ text: string;
53
+ turnId?: number;
54
+ }
44
55
  export interface PlaybookCallRequest {
45
56
  callId: string;
46
57
  playbookId: string;
@@ -106,6 +117,7 @@ export interface PlaybookSession {
106
117
  parentSessionId?: string;
107
118
  parentCallId?: string;
108
119
  depth: number;
120
+ playerSessions?: PlayerSessionStore;
109
121
  ports: PlaybookPorts;
110
122
  }
111
123
  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';
@@ -130,8 +142,7 @@ export interface PlaybookPendingBossQuestion {
130
142
  question: string;
131
143
  sourceItem?: string;
132
144
  }
133
- export interface PlaybookRuntimeSnapshot {
134
- schemaVersion: 1;
145
+ interface PlaybookRuntimeSnapshotFields {
135
146
  playbookId: string;
136
147
  machine: JsonValue;
137
148
  playerResumeTokens: {
@@ -148,6 +159,13 @@ export interface PlaybookRuntimeSnapshot {
148
159
  state: PlaybookState;
149
160
  pendingBossQuestions: readonly PlaybookPendingBossQuestion[];
150
161
  }
162
+ export type PlaybookRuntimeSnapshot = PlaybookRuntimeSnapshotFields & ({
163
+ schemaVersion: 1;
164
+ suspendedCall?: never;
165
+ } | {
166
+ schemaVersion: 2;
167
+ suspendedCall?: PlaybookSuspendedCall;
168
+ });
151
169
  export interface PlaybookControlAction {
152
170
  id: string;
153
171
  label: string;
@@ -192,3 +210,4 @@ export interface PlaybookRuntime {
192
210
  dispose(): Promise<void>;
193
211
  }
194
212
  export type PlaybookRuntimeFactory<Options = unknown> = (options: Options) => PlaybookRuntime;
213
+ export {};
package/src/runtime.ts CHANGED
@@ -19,6 +19,17 @@ export interface PlayerCallOptions {
19
19
  resume: string | false;
20
20
  }
21
21
 
22
+ // DR-030: a composing host may supply one frame-local view of the root
23
+ // engagement's player continuation. The runtime selects through this store
24
+ // before tracing/calling and updates it from the validated result. Hosts that
25
+ // omit it retain the runtime's private per-session store.
26
+ export interface PlayerSessionStore {
27
+ select(playerId: string): string | false;
28
+ update(playerId: string, resumeToken?: string): void;
29
+ snapshot(): Readonly<Record<string, string>>;
30
+ restore(tokens: Readonly<Record<string, string>>): void;
31
+ }
32
+
22
33
  export interface CaptainCallOptions {
23
34
  visibility: 'visible' | 'hidden';
24
35
  resume: string | false;
@@ -64,6 +75,15 @@ export interface PlaybookPendingCall {
64
75
  childSessionId: string;
65
76
  }
66
77
 
78
+ // DR-031 §5: complete durable identity for one nested call whose start
79
+ // boundary has already been published and whose child remains suspended.
80
+ // `turnId` is absent when the call was opened outside a Boss-turn boundary.
81
+ export interface PlaybookSuspendedCall extends PlaybookPendingCall {
82
+ stateId: string;
83
+ text: string;
84
+ turnId?: number;
85
+ }
86
+
67
87
  export interface PlaybookCallRequest {
68
88
  callId: string;
69
89
  playbookId: string;
@@ -143,6 +163,7 @@ export interface PlaybookSession {
143
163
  parentSessionId?: string;
144
164
  parentCallId?: string;
145
165
  depth: number;
166
+ playerSessions?: PlayerSessionStore;
146
167
  ports: PlaybookPorts;
147
168
  }
148
169
 
@@ -187,12 +208,11 @@ export interface PlaybookPendingBossQuestion {
187
208
  sourceItem?: string;
188
209
  }
189
210
 
190
- // DR-014 §1: JSON-safe capture of a parked session. `machine` is the
191
- // XState persisted snapshot and is opaque to hosts; the pending Boss
192
- // questions are first-class so a host can surface what was asked
193
- // without parsing status lines or telemetry.
194
- export interface PlaybookRuntimeSnapshot {
195
- schemaVersion: 1;
211
+ // DR-014 §1 / DR-031 §5: JSON-safe capture of a parked or nested-call
212
+ // suspended session. `machine` is the opaque XState persisted snapshot;
213
+ // pending Boss questions and a schema-2 suspended call are first-class so a
214
+ // host never has to reconstruct durable ownership from presentation records.
215
+ interface PlaybookRuntimeSnapshotFields {
196
216
  playbookId: string;
197
217
  machine: JsonValue;
198
218
  playerResumeTokens: { readonly [playerId: string]: string };
@@ -208,6 +228,18 @@ export interface PlaybookRuntimeSnapshot {
208
228
  pendingBossQuestions: readonly PlaybookPendingBossQuestion[];
209
229
  }
210
230
 
231
+ export type PlaybookRuntimeSnapshot = PlaybookRuntimeSnapshotFields &
232
+ (
233
+ | {
234
+ schemaVersion: 1;
235
+ suspendedCall?: never;
236
+ }
237
+ | {
238
+ schemaVersion: 2;
239
+ suspendedCall?: PlaybookSuspendedCall;
240
+ }
241
+ );
242
+
211
243
  // DR-029: one currently valid, runtime-advertised control action. The id
212
244
  // is stable within the returned view; the label is runtime-written,
213
245
  // Boss-appropriate text derived from source state descriptions.
@@ -67,6 +67,11 @@ export interface ScheduledStatus {
67
67
  message: string;
68
68
  data?: JsonValue;
69
69
  }
70
+ /** Boss-facing identity for one FSM state whose invoked actor is `player`. */
71
+ export interface XStatePlayerStateStatus {
72
+ player: string;
73
+ label: string;
74
+ }
70
75
  export interface XStateBossEventFieldSpec {
71
76
  /** The judge supplies routing data; the runtime supplies exact Boss text. */
72
77
  source: 'judge' | 'text';
@@ -177,8 +182,10 @@ export interface XStatePlaybookRuntimeSpec<TOptions> {
177
182
  * recoverable FSM-result failures instead.
178
183
  */
179
184
  captainStrategy?: XStateCaptainStrategy<TOptions>;
180
- /** Status line emitted after classification names an event. Default: none. */
185
+ /** Status line emitted after classification; canonical metadata defaults to the event type, legacy artifacts to none. */
181
186
  classificationStatus?: (event: EventObject) => string | undefined;
187
+ /** Complete FSM-derived Boss-facing metadata for every `player` state; its presence selects the canonical status profile. */
188
+ playerStates?: Readonly<Record<string, XStatePlayerStateStatus>>;
182
189
  /** Map a player-invoking state's input to the host player id. Default: lowercased player name. */
183
190
  resolvePlayerId?: (input: PlaybookPlayerInput, options: TOptions) => string;
184
191
  /** Compose the player prompt. Default: continuation blocks + `<field>` placeholder substitution. */
@@ -207,7 +214,7 @@ export interface XStatePlaybookRuntimeSpec<TOptions> {
207
214
  controlContextFields?: readonly string[];
208
215
  /** States that may suspend for a Boss reply. Default: targets of the FSM's `awaitBossReply` BOSS_REPLY transitions. */
209
216
  resumableStateIds?: ReadonlySet<string>;
210
- /** Human status lines for a root transition. Default: entry lines with question/failure surfacing. */
217
+ /** Human status lines for a root transition. Default: guard, declared-player, question, and failure lines. */
211
218
  statusesForState?: (state: PlaybookState, context: Record<string, unknown>, event: unknown) => readonly ScheduledStatus[];
212
219
  /** Detached JSON-safe transition-event descriptor. Default: `type` + `transitionEventFields` strings + validated output + normalized error. */
213
220
  normalizeTransitionEvent?: (event: unknown) => JsonValue | undefined;