@sublang/playbook 13.3.0 → 14.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 +5 -5
- package/docs/configuration.md +10 -2
- package/docs/embedding.md +20 -1
- package/package.json +6 -1
- package/reference/sdlc/code.playbook/bin/launch-config.js +88 -5
- package/reference/sdlc/code.playbook/bin/playbook.js +4 -0
- package/reference/sdlc/code.playbook/bin/repository-effects.js +11 -3
- package/reference/sdlc/code.playbook/bin/run.js +4 -0
- package/reference/sdlc/code.playbook/bin/session-store.js +2 -2
- package/reference/sdlc/code.playbook/playbook-captain.js +17 -4
- package/reference/sdlc/code.playbook/playbook-captain.ts +19 -4
- package/reference/sdlc/dev.playbook/dev.playbook.d.ts +3 -10
- package/reference/sdlc/dev.playbook/dev.playbook.js +5 -5
- package/reference/sdlc/dev.playbook/dev.playbook.ts +7 -7
- package/reference/sdlc/pr.md +17 -4
- package/reference/sdlc/pr.playbook/pr.fsm.d.ts +17 -3
- package/reference/sdlc/pr.playbook/pr.fsm.js +49 -18
- package/reference/sdlc/pr.playbook/pr.fsm.ts +63 -20
- package/reference/sdlc/pr.playbook/pr.gears.md +20 -7
- package/reference/sdlc/pr.playbook/pr.playbook.js +2 -1
- package/reference/sdlc/pr.playbook/pr.playbook.ts +2 -1
- package/slc/experiments/fsm-scaffold-guidance.md +31 -0
- package/slc/gears2fsm.md +145 -58
- package/slc/link.md +158 -7
- package/slc/materialize-link.mjs +460 -0
- package/slc/optimize.md +46 -6
- package/slc/scaffold-fsm.mjs +225 -0
- package/slc/slc.pin-inputs.json +13 -0
- package/slc/text2gears.md +38 -3
- package/slc/workflow-contracts.json +371 -0
- package/src/xstate-playbook-runtime.js +35 -1
- package/src/xstate-playbook-runtime.ts +54 -1
package/slc/gears2fsm.md
CHANGED
|
@@ -54,8 +54,10 @@ inference. Helpers that construct transition arrays shall preserve guard,
|
|
|
54
54
|
action, and target literals with `as const`, `satisfies`, or typed action/guard
|
|
55
55
|
functions rather than widening registered names to plain `string`.
|
|
56
56
|
|
|
57
|
-
The artifact shall not
|
|
58
|
-
implementations.
|
|
57
|
+
The artifact shall not bind or construct a runner or bake in concrete actor
|
|
58
|
+
implementations. A named stateless public boundary validator is permitted;
|
|
59
|
+
importing it shall not construct a runtime, bind host capabilities, or call ports.
|
|
60
|
+
Each actor placeholder shall fail explicitly (for example,
|
|
59
61
|
throw `'captain actor must be provided by the runner'`).
|
|
60
62
|
Where the Source artifact begins with an SPDX comment block, the generated
|
|
61
63
|
artifact shall preserve its license and copyright text before the imports
|
|
@@ -83,7 +85,15 @@ comment delimiters into a TypeScript target.
|
|
|
83
85
|
- `stateId`: the stable id of the invoking working leaf;
|
|
84
86
|
- `sourceItem`: the GEARS item ID this state realizes;
|
|
85
87
|
- `command`: the script item's blockquote text, verbatim after Markdown
|
|
86
|
-
unescaping
|
|
88
|
+
unescaping, with every runtime-value placeholder it carries bound from typed
|
|
89
|
+
machine context — a script has no prose contract to carry the field
|
|
90
|
+
separately, and the provided actor executes `command` verbatim.
|
|
91
|
+
A bound value is data, never syntax: the placeholder occupies a
|
|
92
|
+
single-quoted shell word, and binding replaces that whole word — quotes
|
|
93
|
+
included — with the value encoded as its own single-quoted literal, each
|
|
94
|
+
embedded `'` written `'\''`. Interpolating a value into double-quoted or
|
|
95
|
+
unquoted command text is malformed, because a retained value is reported
|
|
96
|
+
text that would otherwise expand or run as shell syntax;
|
|
87
97
|
- `result`: a record whose keys are the item's two declared guard names, first
|
|
88
98
|
the zero-exit guard, then the nonzero-exit guard.
|
|
89
99
|
|
|
@@ -119,19 +129,28 @@ Nested-playbook output, completed-result evidence, plans, context, and machine
|
|
|
119
129
|
output shall use that readonly type rather than a mutable array/record
|
|
120
130
|
near-duplicate. The linker shall not cast or copy around a variance mismatch.
|
|
121
131
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
Except for a Source-declared local-role prompt-identity placeholder, every
|
|
133
|
+
runtime-value placeholder established by Source in a direct-Captain or
|
|
134
|
+
delegated-player prompt shall be backed by a typed ordinary actor-input field
|
|
135
|
+
populated from typed machine context, so the linker can substitute it with the
|
|
136
|
+
exact runtime value. Angle-bracketed metavariables quoted inside domain
|
|
137
|
+
instructions (for example the literal `<model>` in a commit-message format)
|
|
138
|
+
remain ordinary prompt text and are not runtime-value placeholders. For the
|
|
139
|
+
generic Captain forms, wire `<boss-intent>` from `bossIntent`,
|
|
129
140
|
`<enabled-playbooks>` from `enabledPlaybooks`, `<remaining-plan>` from
|
|
130
141
|
`remainingPlan`, and `<completed-call-results>` from
|
|
131
|
-
`completedCallResults`. Other placeholders shall retain the
|
|
132
|
-
field established by Source (for example `<#>` from
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
`completedCallResults`. Other non-identity placeholders shall retain the
|
|
143
|
+
semantic typed field established by Source (for example `<#>` from
|
|
144
|
+
`irNumber`). Leaving an ordinary runtime-value placeholder literal, replacing
|
|
145
|
+
it with an empty default because its field was omitted, or making the linker
|
|
146
|
+
recover it from untyped context is malformed.
|
|
147
|
+
The corresponding `invoke.input` object shall include that field beside `prompt`; storing it only in machine context does not satisfy the actor-input contract.
|
|
148
|
+
When Source declares a placeholder as the current identity of a local acting
|
|
149
|
+
role, preserve the placeholder literal in the FSM prompt and do not add any
|
|
150
|
+
identity-value field to machine input, runtime options, machine context, or
|
|
151
|
+
actor input, required or optional. The linker shall resolve the placeholder at
|
|
152
|
+
prompt-composition time by calling the invocation-scoped
|
|
153
|
+
`promptIdentity(roleId)` lookup for the declared local role identified by Source.
|
|
135
154
|
The sole blockquote placeholder of a dynamic nested-playbook item is instead
|
|
136
155
|
the child `textContext` field specified in §Nested playbook calls.
|
|
137
156
|
|
|
@@ -261,14 +280,16 @@ Each state shall declare:
|
|
|
261
280
|
|
|
262
281
|
The source item ID shall live in `invoke.input.sourceItem`, not in a comment — this keeps the GEARS-to-state mapping machine-readable.
|
|
263
282
|
|
|
283
|
+
Public `meta.playbook` state metadata belongs only to nodes declared under `states`; the machine root shall omit `meta.playbook`, while its XState `id`, description, and metadata outside that namespace remain unrestricted.
|
|
264
284
|
Outside a parallel group's regions, a state's `meta.playbook.stateId` shall equal its state key — the one identity a factory-backed linked runtime indexes by.
|
|
265
285
|
A delegated state's `invoke.input.role` shall match the canonical lowercase id of its source item's named role.
|
|
266
286
|
A direct Captain state shall not invent a `Captain` role binding.
|
|
267
287
|
|
|
268
|
-
Every
|
|
269
|
-
|
|
270
|
-
derives busyness strictly from active-state tags, so an untagged
|
|
271
|
-
reads as quiescent while its call is still in
|
|
288
|
+
Every direct-Captain, delegated-player, or script working leaf — sequential
|
|
289
|
+
or parallel — shall carry the tag `playbook.busy`: the shared quiescence
|
|
290
|
+
helper derives busyness strictly from active-state tags, so an untagged
|
|
291
|
+
Captain, player, or script leaf reads as quiescent while its call is still in
|
|
292
|
+
flight.
|
|
272
293
|
|
|
273
294
|
The machine's initial state shall be a quiescent idle hub (no `invoke`) — typically `ready` — that accepts the Boss entry events and carries the `playbook.parked` tag because it can return control to Boss.
|
|
274
295
|
Captain- and player-invoking work begins only on a Boss-originated event, so
|
|
@@ -315,7 +336,12 @@ An item that prompts or relays to a named role shall map to exactly one `player`
|
|
|
315
336
|
A nested-call item shall map to exactly one `playbook` invocation.
|
|
316
337
|
A script item (`Captain shall run:`) shall map to
|
|
317
338
|
exactly one `script` invocation whose `input.command` carries the blockquote
|
|
318
|
-
|
|
339
|
+
with its runtime-value placeholders substituted, and whose `result` preserves
|
|
340
|
+
the item's two guards in declared order.
|
|
341
|
+
Where the substituted value is absent, the compiled input shall keep the
|
|
342
|
+
placeholder literal rather than an empty default, so the command compares
|
|
343
|
+
against text no output can equal and the step refuses instead of acting on an
|
|
344
|
+
unbound target.
|
|
319
345
|
The compiler shall not infer one actor kind from a
|
|
320
346
|
runtime player name or encode Captain as a player.
|
|
321
347
|
A script state is not agent-invoking: the compiler shall not add
|
|
@@ -369,6 +395,28 @@ the sibling invocations automatically.
|
|
|
369
395
|
|
|
370
396
|
## Nested playbook calls
|
|
371
397
|
|
|
398
|
+
The independently packaged [workflow contracts](workflow-contracts.json) describe
|
|
399
|
+
only the public outputs of builtin dependencies from `@sublang/playbook`.
|
|
400
|
+
This pipeline adopts the catalog's `literalTargetBindings` as its default
|
|
401
|
+
nested-target namespace. An explicit Source or supplied compiler-dependency
|
|
402
|
+
binding overrides a default and requires the replacement's own public interface.
|
|
403
|
+
Runtime hosts shall honor the compiled dependency bindings as external ABIs.
|
|
404
|
+
When the call target is bound to one of those packaged builtins, read its exact
|
|
405
|
+
output interface before compiling a Source-authored acceptance or relay
|
|
406
|
+
predicate; do not infer field names from the desired meaning or inspect the
|
|
407
|
+
callee implementation. A local basename or a custom target reusing an id does
|
|
408
|
+
not establish that dependency binding.
|
|
409
|
+
The shared bridge correlates the invocation and its supplied input scope;
|
|
410
|
+
the builtin REVIEW result attests to that scope with `noUnsettledFindings: true`
|
|
411
|
+
and `evaluatedRevision`. Do not invent `reviewedCommit` or require the final
|
|
412
|
+
evaluated revision to equal the caller's earlier commit, since REVIEW may make
|
|
413
|
+
its own fixes. Retain every Source-authored predicate on the declared fields.
|
|
414
|
+
If an output-dependent call needs an external interface that is unavailable or
|
|
415
|
+
inconsistent with the declared dependency, report `BLOCKED` with that missing
|
|
416
|
+
compiler input rather than inventing fields or asking the source author to
|
|
417
|
+
repair sufficient domain behavior. Missing or contradictory authored behavior
|
|
418
|
+
still requires the host's source-clarification protocol.
|
|
419
|
+
|
|
372
420
|
An item whose behavior is a literal or dynamic
|
|
373
421
|
`Captain shall call playbook ...:` shall compile to a state that invokes a
|
|
374
422
|
typed `playbook` actor, not the `captain` or `player` actor.
|
|
@@ -416,8 +464,13 @@ parsing the `invoke.input` function's source. The evaluated `playbookId` and
|
|
|
416
464
|
corresponding metadata property. Literal calls need not carry these dynamic
|
|
417
465
|
metadata properties and retain their existing behavior.
|
|
418
466
|
|
|
419
|
-
The call state shall carry tag `playbook.suspended
|
|
420
|
-
|
|
467
|
+
The call state shall carry tag `playbook.suspended`.
|
|
468
|
+
The call state and every ancestor state, including the machine root, shall omit
|
|
469
|
+
tag `playbook.busy`.
|
|
470
|
+
An independently active sibling Captain, player, or script leaf may carry
|
|
471
|
+
`playbook.busy`.
|
|
472
|
+
The call state shall route `invoke.onDone` from child output and
|
|
473
|
+
`invoke.onError` from child failure.
|
|
421
474
|
The child call shall remain state-scoped: leaving the call state stops the
|
|
422
475
|
invoked actor and aborts the host call through XState's invocation signal
|
|
423
476
|
[[2]].
|
|
@@ -447,9 +500,12 @@ result, the error action shall inspect whether its status was `aborted` or
|
|
|
447
500
|
`error`; it shall not collapse both into an invented success/failure enum. The
|
|
448
501
|
FSM may inspect that public structural data without importing the runner or
|
|
449
502
|
constructing runtime call identities.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
503
|
+
`onDone` proves successful bridge delivery without a declared child failure;
|
|
504
|
+
it does not establish every caller-owned domain condition. The caller shall
|
|
505
|
+
enforce its own explicit Source-authored acceptance or relay predicates on
|
|
506
|
+
the delivered output before continuing, without inventing predicates from
|
|
507
|
+
callee implementation details or overriding the child's compiled terminal
|
|
508
|
+
kind with output fields.
|
|
453
509
|
For a workflow that reassesses child results, use a typed JSON-safe record such
|
|
454
510
|
as `{ playbookId, status: 'ok', output }` on `onDone` and
|
|
455
511
|
`{ playbookId, status: 'aborted' | 'error', error }` on `onError`, with a
|
|
@@ -466,36 +522,47 @@ been created. On success, persist only `event.output`, which is the actual child
|
|
|
466
522
|
machine output returned by the bridge, not a runtime call-result envelope.
|
|
467
523
|
When that optional output is absent, omit the `output` property from the
|
|
468
524
|
completed-result record rather than storing `undefined`.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
525
|
+
Recognize authored rejected child results with the existing named
|
|
526
|
+
`validatePlaybookCallResult` export from `@sublang/playbook/xstate-runtime`
|
|
527
|
+
and the type-only `PlaybookCallResult` from `@sublang/playbook/runtime`:
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
import { validatePlaybookCallResult } from '@sublang/playbook/xstate-runtime';
|
|
531
|
+
import type { PlaybookCallResult } from '@sublang/playbook/runtime';
|
|
532
|
+
|
|
533
|
+
export function authoredChildResult(error: unknown, expectedPlaybookId: string): PlaybookCallResult | undefined {
|
|
534
|
+
if (!(error instanceof Error)) return undefined;
|
|
535
|
+
try {
|
|
536
|
+
const result = validatePlaybookCallResult(
|
|
537
|
+
(error as Error & { result?: unknown }).result,
|
|
538
|
+
expectedPlaybookId,
|
|
539
|
+
);
|
|
540
|
+
return result.status !== 'ok' || result.terminal?.kind === 'failure' ? result : undefined;
|
|
541
|
+
} catch {
|
|
542
|
+
return undefined;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Pass the actual selected target id from the same source-owned target used by
|
|
548
|
+
`invoke.input`; do not fabricate a request or child-session identity for this
|
|
549
|
+
check. The shared bridge owns invocation correlation. Its existing validator
|
|
550
|
+
owns the complete [public result union](link.md#playbookports-contract),
|
|
551
|
+
including terminal/state shapes, optional abort error, and JSON validity;
|
|
552
|
+
do not rewrite those structural checks or redeclare their public types.
|
|
553
|
+
An invalid result returns no authored outcome and takes the existing
|
|
554
|
+
control-error fallback. A validated successful child output still needs its
|
|
555
|
+
separate Source-owned acceptance predicate on `onDone`.
|
|
556
|
+
After preserving Source-authored success acceptance and recovery cases plus the
|
|
557
|
+
public control-error `onError` fallback, omit an `onDone` transition whose guard
|
|
558
|
+
cannot be reached from any legal predecessor/context after the prior ordered
|
|
559
|
+
`onDone` arms.
|
|
560
|
+
This rule does not require arbitrary finite enumeration, drop valid failure
|
|
561
|
+
behavior, or relax verifier obligations.
|
|
562
|
+
Validate the full public result before projecting only the permitted compact
|
|
563
|
+
evidence; evidence minimization shall not narrow the accepted public union.
|
|
564
|
+
The helper supplies no workflow route, child-domain predicate, runner or actor
|
|
565
|
+
implementation. Its ordinary package import is an explicit artifact dependency.
|
|
499
566
|
|
|
500
567
|
Before entering a dynamic call, the machine shall reject an empty target and
|
|
501
568
|
empty input text, any target equal to `selfPlaybookId`, and any target that
|
|
@@ -551,10 +618,13 @@ The artifact shall not bake them into machine input, options, or context; model
|
|
|
551
618
|
Host-owned configuration such as an enabled-playbook catalog shall remain
|
|
552
619
|
immutable machine input/context for the session. Boss events and actor outputs
|
|
553
620
|
shall not carry, replace, append to, or otherwise overwrite that catalog.
|
|
554
|
-
A placeholder whose value Source assigns to the host — for
|
|
555
|
-
`<definition>` a phase host supplies to a compiled phase — is such
|
|
556
|
-
configuration: a required machine `input` field carried into typed
|
|
557
|
-
the acting actor's input, never a Boss-event or actor-output
|
|
621
|
+
A non-identity placeholder whose value Source assigns to the host — for
|
|
622
|
+
example the `<definition>` a phase host supplies to a compiled phase — is such
|
|
623
|
+
host-owned configuration: a required machine `input` field carried into typed
|
|
624
|
+
context and the acting actor's input, never a Boss-event or actor-output
|
|
625
|
+
payload. A Source-declared local-role prompt-identity placeholder is the
|
|
626
|
+
explicit exception; it resolves from the invocation-scoped `promptIdentity`
|
|
627
|
+
lookup and is not persisted as host configuration.
|
|
558
628
|
Every machine with a dynamic call shall receive its own registered or authored
|
|
559
629
|
playbook id as immutable machine input/context named `selfPlaybookId`, and its
|
|
560
630
|
dynamic-call guard shall reject that target. The leaf-level `stateId` name is
|
|
@@ -588,6 +658,8 @@ while an actual back-edge is rejected.
|
|
|
588
658
|
A transition fires on an event — typically `onDone` (actor completed) [[4]].
|
|
589
659
|
When multiple are possible, a synchronous guard [[5]] picks the path.
|
|
590
660
|
Transitions shall persist relevant typed fields from `event.output` to context via `assign` [[6]] so downstream prompts can read them.
|
|
661
|
+
Where a Source outcome's availability condition is deterministically knowable from execution state, such as a prior Boss reply already received by the machine, the transition shall enforce that condition with authored guards or typed state before the result can be accepted; stating the condition only in a Results description, Judge prose, or prompt text is not enough.
|
|
662
|
+
The compiler shall update or reset those source-owned facts only at their actual Source lifecycle boundaries, and shall not attempt to mechanically decide semantic judgments that Source leaves to the acting agent.
|
|
591
663
|
Transitions shall be self-driving when source items define the next obligation.
|
|
592
664
|
Routing to an idle hub is for recovery, unrecoverable Boss input, or one-shot entry events — not the happy path.
|
|
593
665
|
|
|
@@ -647,6 +719,15 @@ contract shall require exactly `targetId: 'routing'` plus the fresh
|
|
|
647
719
|
`BOSS_INTERRUPT` jumps into an **active** machine, pre-empting whichever state is running.
|
|
648
720
|
**Boss entry events** start or resume from idle or recoverable states when Boss-supplied parameters can't be inferred from machine state alone.
|
|
649
721
|
Entry events shall be typed alongside `BOSS_INTERRUPT` and populate context via a dedicated action.
|
|
722
|
+
Boss text supplied by an entry event shall not become a required machine-construction input unless Source independently requires that value before the first Boss turn; an optional source-appropriate seed may remain.
|
|
723
|
+
A generated required input annotation alone is not evidence of that independent Source bootstrap requirement.
|
|
724
|
+
Entry guards run before entry copy actions: validate supplied text from the
|
|
725
|
+
current event, not solely from the context that action will populate.
|
|
726
|
+
For an omitted event value, use an existing context seed only where Source
|
|
727
|
+
permits it; do not manufacture a required constructor value to satisfy a
|
|
728
|
+
context-only entry guard. The linked runtime sends the entry event normally;
|
|
729
|
+
its `entryEvent.contextField` declaration supports failure retry and does not
|
|
730
|
+
copy fresh text into context before the guard.
|
|
650
731
|
An entry event's copy action shall not clear per-run parameters the event omits: an absent optional field falls back to the existing (input-seeded) context value.
|
|
651
732
|
The two surfaces shall not be collapsed. `BOSS_INTERRUPT` always carries its
|
|
652
733
|
target id and may additionally carry typed Boss-supplied fields such as an
|
|
@@ -698,6 +779,9 @@ A delegated `PlayerInput` shall produce the role asker with its canonical local
|
|
|
698
779
|
Only
|
|
699
780
|
`question` shall come from adjudicated actor output.
|
|
700
781
|
|
|
782
|
+
The canonical storage paths are `context.pendingBossQuestion` and `context.bossReply` for the scalar form, or `context.pendingBossQuestions[stateId]` and `context.bossReplies[stateId]` for the keyed form.
|
|
783
|
+
A private wrapper such as `context.continuation` shall not replace these fields directly on machine context.
|
|
784
|
+
|
|
701
785
|
A machine with at most one active Captain or player task may use the scalar
|
|
702
786
|
form:
|
|
703
787
|
|
|
@@ -740,6 +824,9 @@ A captain- or player-invoking state's `invoke.input` function shall carry the
|
|
|
740
824
|
pending question and reply selected for that working leaf as singular
|
|
741
825
|
`pendingBossQuestion` and `bossReply` fields, regardless of the scalar or keyed
|
|
742
826
|
context representation, so prompt composition has one stable contract.
|
|
827
|
+
When Source requires earlier discussion or constraints on a later invocation, the compiler shall persist that source-owned history in serializable machine context before replacing or clearing the pending Q/A fields.
|
|
828
|
+
The later `invoke.input` shall relay that persisted context on both resumed and fresh calls that Source says need it, preserving Source-owned relevance and format without imposing all-history semantics on sources that do not require it.
|
|
829
|
+
Shared Boss-reply continuation carries only the latest Q/A pair, and a backend continuation token is not durable Source history.
|
|
743
830
|
When both fields are present, the linked runtime shall compose the continuation preamble and labelled Q&A blocks per [link.md "Player prompt composition"](link.md#player-prompt-composition).
|
|
744
831
|
The FSM artifact shall not bake the continuation preamble into the GEARS-derived `prompt` body.
|
|
745
832
|
|
package/slc/link.md
CHANGED
|
@@ -24,6 +24,9 @@ Hosts are out of scope for this phase.
|
|
|
24
24
|
Each host has an adapter that loads a `PlaybookRuntime` module and supplies the host's primitives as `PlaybookPorts`.
|
|
25
25
|
The adapter shall speak only `PlaybookPorts` to the runtime and shall not leak host types back into it.
|
|
26
26
|
|
|
27
|
+
At construction, the shared factory validates linked metadata and the shared construction shape; the Captain host owns registry-manifest and live authority-envelope validation at its construction boundary.
|
|
28
|
+
Do not audit the bare shared factory as if it owned that Captain-host boundary or synthesize host capabilities in the emitted artifact.
|
|
29
|
+
|
|
27
30
|
The link compiler shall not modify the FSM artifact and shall not re-derive Captain prompts, result keys, or guard semantics — those are fixed by the FSM.
|
|
28
31
|
|
|
29
32
|
## Formats
|
|
@@ -33,6 +36,138 @@ The link compiler shall not modify the FSM artifact and shall not re-derive Capt
|
|
|
33
36
|
| source | fsm | .ts |
|
|
34
37
|
| target | playbook | .ts |
|
|
35
38
|
|
|
39
|
+
## Optional deterministic materialization
|
|
40
|
+
|
|
41
|
+
For an ordinary flat workflow using only `player` and `script` actors,
|
|
42
|
+
the supported prompt profile below, shared remaining strategies, and primitive configured options, the linker may
|
|
43
|
+
use the adjacent `materialize-link.mjs` tool to emit the thin module.
|
|
44
|
+
The complete definition below remains binding; the tool replaces repetitive
|
|
45
|
+
module generation, not semantic analysis or emitted conformance verification.
|
|
46
|
+
Read the actual FSM and supply every erased or authored contract exactly.
|
|
47
|
+
Do not use `flat-defaults` or `flat-quoted-relays` when another custom composer, classifier, required-field
|
|
48
|
+
extractor, session-derived input mapping, controller strategy, nested call,
|
|
49
|
+
parallel state, or compound state is needed.
|
|
50
|
+
|
|
51
|
+
Invoke the tool with the actual definition directory, source FSM, and declared
|
|
52
|
+
target; supply a JSON descriptor on standard input:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
node "<definition-directory>/materialize-link.mjs" --fsm "<source.fsm.ts>" --out "<target.playbook.ts>" <<'JSON'
|
|
56
|
+
{
|
|
57
|
+
"schema": "sublang.playbook.link.v1",
|
|
58
|
+
"profile": "flat-defaults",
|
|
59
|
+
"machineExport": "exampleMachine",
|
|
60
|
+
"label": "EXAMPLE",
|
|
61
|
+
"options": {},
|
|
62
|
+
"inputMapping": {},
|
|
63
|
+
"entryEvent": { "type": "BOSS_TASK", "textField": "bossIntent", "contextField": "bossIntent" },
|
|
64
|
+
"bossEvents": [],
|
|
65
|
+
"outcomeAuthority": {
|
|
66
|
+
"governedPlayerStates": {
|
|
67
|
+
"work": {
|
|
68
|
+
"done": { "fields": {}, "repositoryDisposition": "one-descendant-commit" },
|
|
69
|
+
"needsBossReply": { "fields": { "question": "presentation" }, "repositoryDisposition": "deferred" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"placeholderFields": {},
|
|
74
|
+
"transitionEventFields": ["bossIntent", "answer", "questionId"],
|
|
75
|
+
"verbatimPayloadFields": [],
|
|
76
|
+
"resumableStateIds": ["work"],
|
|
77
|
+
"unfinishedFinalStateIds": [],
|
|
78
|
+
"controlContextFields": []
|
|
79
|
+
}
|
|
80
|
+
JSON
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
This descriptor is an example shape, not default workflow semantics.
|
|
84
|
+
Select `flat-defaults` for the unchanged shared player composer, or
|
|
85
|
+
`flat-quoted-relays` when the source requires standalone `> <token>` relay lines.
|
|
86
|
+
The latter uses the same descriptor keys and `placeholderFields` mappings;
|
|
87
|
+
ordinary string tokens substitute literally, while standalone relay values
|
|
88
|
+
quote each nonempty line with `> ` and preserve LF/CRLF and blank lines.
|
|
89
|
+
An empty relay value omits its complete template line; missing or non-string
|
|
90
|
+
values retain the token. Inserted values are never substituted again.
|
|
91
|
+
The emitted composer preserves the installed shared fresh/resumed Q&A prefix
|
|
92
|
+
and does not modify the original input. Labelled relays, identity-specific
|
|
93
|
+
composition, structured renderers, and other custom strategies require
|
|
94
|
+
ordinary linking; do not select either profile for those requirements.
|
|
95
|
+
Every top-level member is required; unknown members are errors.
|
|
96
|
+
`options` maps each configured option to `{ "type": "string" | "number" |
|
|
97
|
+
"boolean", "required": true | false }`; `inputMapping` maps each FSM input
|
|
98
|
+
field to its supplying option name.
|
|
99
|
+
Option unions, closed values, range constraints, nested structures, and
|
|
100
|
+
session-derived input values are outside this profile; do not widen their
|
|
101
|
+
contracts to an unconstrained primitive.
|
|
102
|
+
The tool adds optional string `cwd` for script-bearing machines; it does not
|
|
103
|
+
put `cwd` in FSM input unless the descriptor explicitly maps it.
|
|
104
|
+
An explicit `entryEvent: null` selects the shared classifier only where this
|
|
105
|
+
definition permits no deterministic entry; it does not relax entry rules.
|
|
106
|
+
`bossEvents` retains the exact additional erased event fields, source ownership,
|
|
107
|
+
requiredness, and closed values specified under Output.
|
|
108
|
+
The state/outcome/field authority map, placeholder exceptions, transition and
|
|
109
|
+
verbatim fields, resumable states, unfinished finals, and safe ordered context
|
|
110
|
+
projection retain their exact obligations under Output.
|
|
111
|
+
The tool copies role labels and identities from the machine and never guesses
|
|
112
|
+
result semantics by executing an invocation against invented context.
|
|
113
|
+
|
|
114
|
+
The helper resolves the installed shared engine from the source and target
|
|
115
|
+
locations and refuses differing engine resolutions.
|
|
116
|
+
Its factory preflight checks linked metadata.
|
|
117
|
+
It accepts `.fsm.js` on supported Node versions; `.fsm.ts` requires native
|
|
118
|
+
type stripping (Node 23.6+, or Node 22.18+).
|
|
119
|
+
On success it atomically writes only the declared target after factory
|
|
120
|
+
preflight; run all existing conformance checks afterward.
|
|
121
|
+
Exit 2 reports `unsupported` without changing the target: continue ordinary
|
|
122
|
+
linking under this complete definition.
|
|
123
|
+
Exit 1 reports invalid metadata, loading, preflight, or output failure:
|
|
124
|
+
correct the identified problem before treating linking as successful.
|
|
125
|
+
|
|
126
|
+
### Experimental labelled-string and nested-call profile
|
|
127
|
+
|
|
128
|
+
`flat-labelled-relays` is an unmeasured candidate for flat single-region
|
|
129
|
+
machines with delegated players, scripts, and nested `playbook` calls. It
|
|
130
|
+
requires the installed shared `composePlayerContinuation` API. It leaves
|
|
131
|
+
nested input composition, child targets, result guards, recovery, and terminal
|
|
132
|
+
semantics in the unchanged FSM; the shared factory provides the nested bridge.
|
|
133
|
+
It does not authorize generating or importing maintained workflow artifacts.
|
|
134
|
+
|
|
135
|
+
Select it only for literal string substitution in unchanged player templates,
|
|
136
|
+
including labelled `> Label: <token>` lines and source-declared role-identity
|
|
137
|
+
tokens. In addition to every descriptor member above, supply:
|
|
138
|
+
|
|
139
|
+
- `playerInputExport`: the exact exported FSM player-input type name.
|
|
140
|
+
- `omitEmptyRelayLines`: exact complete source lines to omit only when their
|
|
141
|
+
mapped string value is empty, for example
|
|
142
|
+
`["> Run results: <run-results>"]`. Each line starts with `> ` and ends in
|
|
143
|
+
one placeholder; any preceding label is literal. Use an explicit empty
|
|
144
|
+
array when no line is optional; never infer optionality from a missing value.
|
|
145
|
+
- `identityPlaceholders`: an explicit token-to-canonical-local-role map, such
|
|
146
|
+
as `{ "coder-llm": "coder" }` only where Source requires that identity.
|
|
147
|
+
These values come solely from the invocation-scoped identity lookup, never
|
|
148
|
+
from options, FSM context, or model prose. Use `{}` when none is authored.
|
|
149
|
+
|
|
150
|
+
The composer replaces original tokens once, prefixes each continuation line
|
|
151
|
+
of a value inserted into a quoted source line with `> `, preserves line
|
|
152
|
+
separators and literal inserted text, and leaves missing/non-string values as
|
|
153
|
+
source tokens. Undeclared empty relay lines retain their authored text.
|
|
154
|
+
The emitted verification composer has the same identity and optional third
|
|
155
|
+
resume arguments as the runtime; the shared helper owns fresh/resumed Q&A.
|
|
156
|
+
Structured rendering, custom classifiers/extractors/controllers, direct
|
|
157
|
+
Captain actors, compound/parallel topology, and constrained/nonprimitive
|
|
158
|
+
options remain unsupported. Source-derived metadata and all existing
|
|
159
|
+
conformance checks remain mandatory. This profile changes no runtime bridge,
|
|
160
|
+
workflow semantics, or measured performance claim.
|
|
161
|
+
|
|
162
|
+
For an FSM that satisfies one of the materializer profiles above, derive its
|
|
163
|
+
complete source-owned descriptor and run `materialize-link.mjs` before writing
|
|
164
|
+
an ordinary linked module by hand. Use the supported profile whose composer
|
|
165
|
+
matches the actual source. An unsupported-profile exit continues ordinary
|
|
166
|
+
linking under this definition; invalid metadata must be corrected. Never
|
|
167
|
+
widen an option, omit a required custom strategy, change the FSM, or relax a
|
|
168
|
+
verification check to make a profile fit.
|
|
169
|
+
|
|
170
|
+
|
|
36
171
|
## PlaybookRuntime contract
|
|
37
172
|
|
|
38
173
|
The emitted module shall default-export a factory of the following shape:
|
|
@@ -364,7 +499,7 @@ The roleless session-Captain is the sole signature exception: its public options
|
|
|
364
499
|
|
|
365
500
|
Artifact schema `3` shall require `outcomeAuthority` as an own plain-JSON data property and shall instantiate the shared factory with exactly `{ configuredOptions, hostCapabilities }`, where `configuredOptions` is the registry-validated plain-JSON workflow slice and `hostCapabilities` is a non-null live current-host object.
|
|
366
501
|
For schema `3`, the `Options` argument of the one-argument shared `PlaybookRuntimeFactory<Options>` shall be `XStatePlaybookRuntimeConstruction<ConfiguredOptions, HostCapabilities>`; the registry's public entry receives the two members separately and composes that one internal argument only at the artifact boundary.
|
|
367
|
-
For a Captain-hosted schema-3 artifact, `hostCapabilities` shall contain exactly `authority`, `repository`, and `effectLedger`: authority binds that artifact's id, schema, detached role and cohort declarations, current configured working directory, logical session and lease-owner identities, and canonical worktree; repository exposes that same canonical identity plus host-bound observation, acquisition, exclusive-call, and cohort operations, whose optional live completion mapper may return only detached `finalText`, `semanticCandidate`, `logicalOperationId`,
|
|
502
|
+
For a Captain-hosted schema-3 artifact, `hostCapabilities` shall contain exactly `authority`, `repository`, and `effectLedger`: authority binds that artifact's id, schema, detached role and cohort declarations, current configured working directory, logical session and lease-owner identities, and canonical worktree; repository exposes that same canonical identity plus host-bound observation, acquisition, exclusive-call, and cohort operations, whose optional live completion mapper may return only detached `finalText`, `semanticCandidate`, `logicalOperationId`, additional typed ledger commands for the same atomic completion, one `deferred` binding carrying optional UUID `operationId` plus exact `pendingQuestion` and `playerContinuation`, or literal `unresolved: true`, where `deferred` shall be mutually exclusive with `unresolved`, `logicalOperationId`, and commands; and the ledger exposes its synchronous detached `snapshot(): PlaybookEffectLedger` mirror plus `writeAhead(commands: PlaybookEffectLedgerCommandBatch): Promise<PlaybookEffectLedger>` against the current host's atomic writer.
|
|
368
503
|
Only `configuredOptions` may reach option snapshotting and FSM input.
|
|
369
504
|
The capability object, its callbacks, lease token, and live claim or store handles shall enter neither `PlaybookPorts`, machine input or context, runtime snapshots, launch or durable projections, retained generations, nor continuation identity; the detached ledger data and canonical identities returned by its ledger channel shall instead persist only through the versioned effect-ledger members defined below.
|
|
370
505
|
|
|
@@ -425,6 +560,9 @@ Each state shall name exactly the outcomes in that state's `invoke.input.result`
|
|
|
425
560
|
The outcome key owns the semantic discriminator, so `guard` shall not appear in `fields`; the `fields` keys shall equal every additional payload field named by that outcome's result description.
|
|
426
561
|
Each field shall have exactly one authority from `presentation`, `semantic`, `effect`, or `runtime`; every linker-declared verbatim payload field and `question` shall be `presentation`, `latestCommit` shall be `effect`, and the payload fields `irNumber` and `irTask` shall be `semantic`, while outcome keys such as `moreTasks` and `finalTask` remain semantic discriminators.
|
|
427
562
|
Each repository disposition shall be exactly `unchanged`, `one-descendant-commit`, or `deferred`; an effect-owned field is valid on `one-descendant-commit` and `unchanged` and never on `deferred`, and `deferred` is valid only on `needsBossReply` with presentation-owned `question` and another outcome in that state declaring `one-descendant-commit`.
|
|
563
|
+
The linker shall derive each disposition from the source-derived operation and the outcome's required repository effect, independently of whether its result description declares an effect-owned payload field.
|
|
564
|
+
A completion that requires committing the result to Git shall declare `one-descendant-commit` even when its outcome is simply `done` with `fields: {}`; its eligible `needsBossReply` arm shall declare `deferred` under the rule above.
|
|
565
|
+
The absence of `latestCommit` or any other effect-owned field shall never justify defaulting that completion to `unchanged`.
|
|
428
566
|
The shared factory shall reject every legacy artifact schema and reject schema-3 missing, extra, unknown, wrongly owned, or inconsistent metadata before the affected player call.
|
|
429
567
|
|
|
430
568
|
`init` receives the host-owned playbook session identity and ports, constructs the XState actor with FSM `input` derived from `options`, and starts the actor.
|
|
@@ -459,6 +597,17 @@ remain a required readonly runtime option passed through to machine input; the
|
|
|
459
597
|
linker shall neither invent an empty catalog nor require it to be baked into a
|
|
460
598
|
CLI link option.
|
|
461
599
|
|
|
600
|
+
The linked module shall export public synchronous pure `validateOptions(value: unknown): PlaybookRuntimeOptions` and bind that same function as the shared spec's `snapshotOptions`.
|
|
601
|
+
The validator shall first capture `value === undefined ? {} : value` with the public `snapshotJsonValue` exported by `@sublang/playbook/xstate-runtime`, before reading option members, applying defaults, or constructing a replacement record; only top-level `undefined` is normalized, and non-JSON input rejects through that shared boundary.
|
|
602
|
+
It shall then validate the artifact's actual option shape, requiredness, source-authored defaults, unknown keys, and declared values against the detached snapshot, reject null and invalid options, and return a detached immutable plain-JSON option record without runtime construction or live host capabilities.
|
|
603
|
+
Boss text supplied by the entry event is not a required startup option unless Source independently requires it before the first Boss turn; a generated required type annotation alone is not that evidence.
|
|
604
|
+
A generated entry guard that requires the text already in context is likewise
|
|
605
|
+
not independent Source evidence; it is a producer defect when the entry action
|
|
606
|
+
has yet to copy the event's text. Do not compensate with a required option or
|
|
607
|
+
invented startup task. `entryEvent.contextField` supplies failure-retry text;
|
|
608
|
+
it does not populate fresh entry context before FSM guards execute.
|
|
609
|
+
A source-appropriate optional seed may remain, and genuine required bootstrap catalogs or other options shall not be erased or filled with invented defaults.
|
|
610
|
+
|
|
462
611
|
Concrete player binding and prompt identity are host policy and shall not enter `PlaybookRuntimeOptions`, machine input, or the emitted artifact.
|
|
463
612
|
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.
|
|
464
613
|
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.
|
|
@@ -1457,10 +1606,12 @@ artifact declares its reached final state's kind: the runtime reads
|
|
|
1457
1606
|
`meta.playbook.terminal` and that state's authored description from the
|
|
1458
1607
|
artifact, never from an agent reply, and a declared value other than `success`
|
|
1459
1608
|
or `failure` is a control-plane error rather than a child outcome.
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1609
|
+
`onDone` proves successful bridge delivery without a declared child failure;
|
|
1610
|
+
it does not establish every caller-owned domain condition. The caller shall
|
|
1611
|
+
enforce its own explicit Source-authored acceptance or relay predicates on
|
|
1612
|
+
the delivered output before continuing, without inventing predicates from
|
|
1613
|
+
callee implementation details or overriding the child's compiled terminal
|
|
1614
|
+
kind with output fields.
|
|
1464
1615
|
Unknown, duplicate, or stale call ids reject without changing actor state.
|
|
1465
1616
|
The finish trace shall therefore precede any parent FSM transition caused by
|
|
1466
1617
|
the child return.
|
|
@@ -2250,8 +2401,8 @@ The thin emitted module:
|
|
|
2250
2401
|
by `PlaybookSession` or another linker-owned source (§PlaybookRuntime
|
|
2251
2402
|
contract), plus the optional `cwd` option whenever the FSM contains a
|
|
2252
2403
|
`script` state (§Script execution).
|
|
2253
|
-
-
|
|
2254
|
-
|
|
2404
|
+
- Exports the public `validateOptions` function specified above and supplies
|
|
2405
|
+
it as the spec's identical `snapshotOptions` callback: validate and JSON-snapshot the
|
|
2255
2406
|
caller's options, rejecting undeclared keys and non-conforming values, so
|
|
2256
2407
|
the factory binds an immutable options record before constructing any
|
|
2257
2408
|
actor.
|