@sublang/playbook 8.0.0 → 10.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 +3 -3
- package/docs/cli.md +66 -23
- package/docs/configuration.md +13 -8
- package/docs/embedding.md +45 -14
- package/package.json +7 -3
- package/reference/sdlc/captain.md +14 -10
- package/reference/sdlc/captain.playbook/captain.fsm.d.ts +33 -13
- package/reference/sdlc/captain.playbook/captain.fsm.js +80 -9
- package/reference/sdlc/captain.playbook/captain.fsm.ts +137 -18
- package/reference/sdlc/captain.playbook/captain.gears.md +10 -6
- package/reference/sdlc/captain.playbook/captain.playbook.d.ts +5 -1
- package/reference/sdlc/captain.playbook/captain.playbook.js +151 -10
- package/reference/sdlc/captain.playbook/captain.playbook.ts +200 -14
- package/reference/sdlc/code.md +0 -1
- package/reference/sdlc/code.playbook/bin/interactive-session.js +170 -17
- package/reference/sdlc/code.playbook/bin/launch-config.js +136 -4
- package/reference/sdlc/code.playbook/bin/playbook.js +81 -4
- package/reference/sdlc/code.playbook/bin/repository-effects.js +2930 -0
- package/reference/sdlc/code.playbook/bin/run.js +365 -63
- package/reference/sdlc/code.playbook/bin/session-store.js +2877 -209
- package/reference/sdlc/code.playbook/code.fsm.d.ts +11 -1
- package/reference/sdlc/code.playbook/code.fsm.js +85 -29
- package/reference/sdlc/code.playbook/code.fsm.ts +95 -33
- package/reference/sdlc/code.playbook/code.gears.md +0 -2
- package/reference/sdlc/code.playbook/code.playbook.d.ts +5 -2
- package/reference/sdlc/code.playbook/code.playbook.js +67 -4
- package/reference/sdlc/code.playbook/code.playbook.ts +87 -8
- package/reference/sdlc/code.playbook/code.registry.d.ts +10 -3
- package/reference/sdlc/code.playbook/code.registry.js +10 -3
- package/reference/sdlc/code.playbook/code.registry.ts +23 -5
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +99 -7
- package/reference/sdlc/code.playbook/playbook-captain.js +1894 -82
- package/reference/sdlc/code.playbook/playbook-captain.ts +2809 -109
- package/reference/sdlc/decide.md +0 -1
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +8 -1
- package/reference/sdlc/decide.playbook/decide.fsm.js +80 -29
- package/reference/sdlc/decide.playbook/decide.fsm.ts +89 -31
- package/reference/sdlc/decide.playbook/decide.gears.md +0 -1
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +15 -5
- package/reference/sdlc/decide.playbook/decide.playbook.js +1994 -191
- package/reference/sdlc/decide.playbook/decide.playbook.ts +3209 -404
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +7 -3
- package/reference/sdlc/decide.playbook/decide.registry.js +10 -3
- package/reference/sdlc/decide.playbook/decide.registry.ts +20 -5
- package/reference/sdlc/review.playbook/review.fsm.d.ts +7 -0
- package/reference/sdlc/review.playbook/review.fsm.js +133 -12
- package/reference/sdlc/review.playbook/review.fsm.ts +140 -12
- package/reference/sdlc/review.playbook/review.playbook.d.ts +5 -2
- package/reference/sdlc/review.playbook/review.playbook.js +78 -4
- package/reference/sdlc/review.playbook/review.playbook.ts +95 -8
- package/reference/sdlc/review.playbook/review.registry.d.ts +10 -3
- package/reference/sdlc/review.playbook/review.registry.js +10 -3
- package/reference/sdlc/review.playbook/review.registry.ts +23 -5
- package/slc/gears2fsm.md +25 -7
- package/slc/link.md +727 -82
- package/src/accepted-outcome.d.ts +18 -0
- package/src/accepted-outcome.js +94 -0
- package/src/accepted-outcome.ts +140 -0
- package/src/runtime.d.ts +165 -3
- package/src/runtime.ts +214 -2
- package/src/xstate-playbook-runtime.d.ts +162 -13
- package/src/xstate-playbook-runtime.js +3344 -564
- package/src/xstate-playbook-runtime.ts +4873 -637
- package/src/xstate-runtime.d.ts +76 -8
- package/src/xstate-runtime.js +1001 -64
- package/src/xstate-runtime.ts +1640 -91
package/slc/link.md
CHANGED
|
@@ -39,7 +39,13 @@ The emitted module shall default-export a factory of the following shape:
|
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
41
|
interface PlaybookRuntime {
|
|
42
|
+
readonly retainedGenerationMetadata?: PlaybookRetainedGenerationMetadata;
|
|
42
43
|
init(session: PlaybookSession): Promise<void>;
|
|
44
|
+
adopt?(
|
|
45
|
+
session: PlaybookSession,
|
|
46
|
+
snapshot: PlaybookRuntimeSnapshot,
|
|
47
|
+
context: PlaybookAdoptionContext,
|
|
48
|
+
): Promise<void>;
|
|
43
49
|
handleBossInput(turn: {
|
|
44
50
|
text: string;
|
|
45
51
|
signal: AbortSignal;
|
|
@@ -49,9 +55,23 @@ interface PlaybookRuntime {
|
|
|
49
55
|
result: PlaybookCallResult;
|
|
50
56
|
signal: AbortSignal;
|
|
51
57
|
}): Promise<PlaybookRunResult>;
|
|
58
|
+
unresolvedEffectEnvelopes?(): readonly (
|
|
59
|
+
| { readonly kind: 'boundary'; readonly boundaryId: string }
|
|
60
|
+
| { readonly kind: 'logical-operation'; readonly operationId: string }
|
|
61
|
+
)[];
|
|
52
62
|
dispose(): Promise<void>;
|
|
53
63
|
}
|
|
54
64
|
|
|
65
|
+
interface PlaybookRetainedGenerationMetadata {
|
|
66
|
+
readonly unfinishedFinalStateIds: readonly string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface PlaybookAdoptionContext {
|
|
70
|
+
readonly sourceSessionId: string;
|
|
71
|
+
readonly sourceGenerationId: string;
|
|
72
|
+
readonly targetChildSessionId?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
55
75
|
interface PlaybookSession {
|
|
56
76
|
sessionId: string;
|
|
57
77
|
playbookId: string;
|
|
@@ -121,10 +141,160 @@ interface PlaybookSuspendedCall extends PlaybookPendingCall {
|
|
|
121
141
|
stateId: string;
|
|
122
142
|
text: string;
|
|
123
143
|
turnId?: number;
|
|
144
|
+
effectBoundaryPrefixSequence?: number | null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
type PlaybookRepositoryReceiptClassification =
|
|
148
|
+
| 'unchanged'
|
|
149
|
+
| 'one-descendant-commit'
|
|
150
|
+
| 'multiple-commits'
|
|
151
|
+
| 'rewritten-or-non-descendant'
|
|
152
|
+
| 'worktree-only-change'
|
|
153
|
+
| 'concurrent-or-foreign-change'
|
|
154
|
+
| 'observation-ambiguous';
|
|
155
|
+
|
|
156
|
+
interface PlaybookRepositoryObservation {
|
|
157
|
+
readonly worktree: string;
|
|
158
|
+
readonly gitDir: string;
|
|
159
|
+
readonly head: string;
|
|
160
|
+
readonly projection: Readonly<Record<string, JsonValue>>;
|
|
161
|
+
readonly projectionDigest: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
interface PlaybookRepositoryReceipt {
|
|
165
|
+
readonly classification: PlaybookRepositoryReceiptClassification;
|
|
166
|
+
readonly baseline: PlaybookRepositoryObservation;
|
|
167
|
+
readonly after?: PlaybookRepositoryObservation;
|
|
168
|
+
readonly commitOid?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
type PlaybookRepositoryDisposition =
|
|
172
|
+
| 'unchanged'
|
|
173
|
+
| 'one-descendant-commit'
|
|
174
|
+
| 'deferred';
|
|
175
|
+
|
|
176
|
+
interface PlaybookEffectBoundary {
|
|
177
|
+
readonly sequence: number;
|
|
178
|
+
readonly boundaryId: string;
|
|
179
|
+
readonly attemptId: string;
|
|
180
|
+
readonly attemptNumber: number;
|
|
181
|
+
readonly playbookId: string;
|
|
182
|
+
readonly runtimeSessionId: string;
|
|
183
|
+
readonly turnId: number;
|
|
184
|
+
readonly callId: string;
|
|
185
|
+
readonly roleId: string;
|
|
186
|
+
readonly sourceStateId: string;
|
|
187
|
+
readonly sourceOutcomeSchema: JsonValue;
|
|
188
|
+
readonly dispositions: readonly PlaybookRepositoryDisposition[];
|
|
189
|
+
readonly canonicalWorktree: {
|
|
190
|
+
readonly worktree: string;
|
|
191
|
+
readonly gitDir: string;
|
|
192
|
+
};
|
|
193
|
+
readonly baseline: PlaybookRepositoryObservation;
|
|
194
|
+
readonly after?: PlaybookRepositoryObservation;
|
|
195
|
+
readonly physicalReceipt?: PlaybookRepositoryReceipt;
|
|
196
|
+
readonly finalText?: string;
|
|
197
|
+
readonly semanticCandidate?: JsonValue;
|
|
198
|
+
readonly initialSemanticCandidate?: JsonValue;
|
|
199
|
+
readonly correctionBudget: { readonly limit: 1; readonly spent: boolean };
|
|
200
|
+
readonly cohortId?: string;
|
|
201
|
+
readonly logicalOperationId?: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
interface PlaybookEffectLogicalOperation {
|
|
205
|
+
readonly sequence: number;
|
|
206
|
+
readonly operationId: string;
|
|
207
|
+
readonly playbookId: string;
|
|
208
|
+
readonly runtimeSessionId: string;
|
|
209
|
+
readonly boundaryIds: readonly string[];
|
|
210
|
+
readonly originalBaseline: PlaybookRepositoryObservation;
|
|
211
|
+
readonly checkpoint?: PlaybookRepositoryObservation;
|
|
212
|
+
readonly pendingQuestion?: PlaybookPendingBossQuestion;
|
|
213
|
+
readonly playerContinuation?: JsonValue;
|
|
214
|
+
readonly checkpointRestorationEligible: boolean;
|
|
215
|
+
readonly logicalReceipt?: PlaybookRepositoryReceipt;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
interface PlaybookEffectLedger {
|
|
219
|
+
readonly schemaVersion: 1;
|
|
220
|
+
readonly revision: number;
|
|
221
|
+
readonly boundaries: readonly PlaybookEffectBoundary[];
|
|
222
|
+
readonly logicalOperations: readonly PlaybookEffectLogicalOperation[];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
type PlaybookEffectBoundaryStart = Omit<
|
|
226
|
+
PlaybookEffectBoundary,
|
|
227
|
+
| 'sequence'
|
|
228
|
+
| 'attemptId'
|
|
229
|
+
| 'attemptNumber'
|
|
230
|
+
| 'after'
|
|
231
|
+
| 'physicalReceipt'
|
|
232
|
+
| 'finalText'
|
|
233
|
+
| 'semanticCandidate'
|
|
234
|
+
| 'initialSemanticCandidate'
|
|
235
|
+
>;
|
|
236
|
+
|
|
237
|
+
type PlaybookEffectLogicalOperationStart = Omit<
|
|
238
|
+
PlaybookEffectLogicalOperation,
|
|
239
|
+
'sequence'
|
|
240
|
+
>;
|
|
241
|
+
|
|
242
|
+
type PlaybookEffectLedgerCommand =
|
|
243
|
+
| {
|
|
244
|
+
readonly kind: 'start-boundaries';
|
|
245
|
+
readonly boundaries: readonly [
|
|
246
|
+
PlaybookEffectBoundaryStart,
|
|
247
|
+
...PlaybookEffectBoundaryStart[],
|
|
248
|
+
];
|
|
249
|
+
}
|
|
250
|
+
| {
|
|
251
|
+
readonly kind: 'replace-boundaries';
|
|
252
|
+
readonly replacements: readonly [
|
|
253
|
+
{
|
|
254
|
+
readonly expected: PlaybookEffectBoundary;
|
|
255
|
+
readonly next: PlaybookEffectBoundary;
|
|
256
|
+
},
|
|
257
|
+
...{
|
|
258
|
+
readonly expected: PlaybookEffectBoundary;
|
|
259
|
+
readonly next: PlaybookEffectBoundary;
|
|
260
|
+
}[],
|
|
261
|
+
];
|
|
262
|
+
}
|
|
263
|
+
| {
|
|
264
|
+
readonly kind: 'append-logical-operations';
|
|
265
|
+
readonly operations: readonly [
|
|
266
|
+
PlaybookEffectLogicalOperationStart,
|
|
267
|
+
...PlaybookEffectLogicalOperationStart[],
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
| {
|
|
271
|
+
readonly kind: 'replace-logical-operations';
|
|
272
|
+
readonly replacements: readonly [
|
|
273
|
+
{
|
|
274
|
+
readonly expected: PlaybookEffectLogicalOperation;
|
|
275
|
+
readonly next: PlaybookEffectLogicalOperation;
|
|
276
|
+
},
|
|
277
|
+
...{
|
|
278
|
+
readonly expected: PlaybookEffectLogicalOperation;
|
|
279
|
+
readonly next: PlaybookEffectLogicalOperation;
|
|
280
|
+
}[],
|
|
281
|
+
];
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
type PlaybookEffectLedgerCommandBatch = readonly [
|
|
285
|
+
PlaybookEffectLedgerCommand,
|
|
286
|
+
...PlaybookEffectLedgerCommand[],
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
interface PlaybookEffectLedgerCapability {
|
|
290
|
+
snapshot(): PlaybookEffectLedger;
|
|
291
|
+
writeAhead(
|
|
292
|
+
commands: PlaybookEffectLedgerCommandBatch,
|
|
293
|
+
): Promise<PlaybookEffectLedger>;
|
|
124
294
|
}
|
|
125
295
|
|
|
126
296
|
interface PlaybookRuntimeSnapshot {
|
|
127
|
-
schemaVersion:
|
|
297
|
+
schemaVersion: 4;
|
|
128
298
|
playbookId: string;
|
|
129
299
|
machine: JsonValue;
|
|
130
300
|
roleResumeTokens: { readonly [roleId: string]: string };
|
|
@@ -138,11 +308,27 @@ interface PlaybookRuntimeSnapshot {
|
|
|
138
308
|
};
|
|
139
309
|
state: PlaybookState;
|
|
140
310
|
pendingBossQuestions: readonly PlaybookPendingBossQuestion[];
|
|
311
|
+
effectLedger: PlaybookEffectLedger;
|
|
312
|
+
/** Original runtime identity retained across schema-3 adoption lineage. */
|
|
313
|
+
retainedEffectSourceSessionId?: string;
|
|
314
|
+
/**
|
|
315
|
+
* Unsafe retained-adoption checkpoint. The marker remains durable until
|
|
316
|
+
* authoritative reconciliation proves its complete suffix replay-safe.
|
|
317
|
+
*/
|
|
318
|
+
retainedEffectReconciliation?: {
|
|
319
|
+
readonly sourceSessionId: string;
|
|
320
|
+
readonly checkpoint: PlaybookEffectLedger;
|
|
321
|
+
};
|
|
322
|
+
failedEffectAttempt?: {
|
|
323
|
+
readonly boundaryPrefix: number;
|
|
324
|
+
readonly attemptId: string | null;
|
|
325
|
+
};
|
|
141
326
|
suspendedCall?: PlaybookSuspendedCall;
|
|
142
327
|
}
|
|
143
328
|
|
|
144
329
|
type PlaybookRunResult =
|
|
145
330
|
| { outcome: 'quiescent' | 'no-action'; state: PlaybookState }
|
|
331
|
+
| { outcome: 'unresolved-effect'; state: PlaybookState }
|
|
146
332
|
| {
|
|
147
333
|
outcome: 'failed' | 'aborted';
|
|
148
334
|
state: PlaybookState;
|
|
@@ -151,6 +337,7 @@ type PlaybookRunResult =
|
|
|
151
337
|
| {
|
|
152
338
|
outcome: 'terminal';
|
|
153
339
|
state: PlaybookState;
|
|
340
|
+
stateDescription?: string;
|
|
154
341
|
output?: JsonValue;
|
|
155
342
|
}
|
|
156
343
|
| {
|
|
@@ -164,11 +351,80 @@ type PlaybookRuntimeFactory<Options = unknown> = (
|
|
|
164
351
|
) => PlaybookRuntime;
|
|
165
352
|
|
|
166
353
|
export default function createPlaybookRuntime(
|
|
167
|
-
|
|
354
|
+
construction: XStatePlaybookRuntimeConstruction<
|
|
355
|
+
PlaybookRuntimeOptions,
|
|
356
|
+
HostCapabilities
|
|
357
|
+
>,
|
|
168
358
|
): PlaybookRuntime;
|
|
169
359
|
```
|
|
170
360
|
|
|
171
|
-
|
|
361
|
+
For a Captain-hosted linked workflow, the default export conforms to `PlaybookRuntimeFactory<XStatePlaybookRuntimeConstruction<PlaybookRuntimeOptions, HostCapabilities>>`, where `HostCapabilities` is the artifact's exact live schema-3 capability type and `PlaybookRuntimeFactory` is the generic factory type the shared contract module exposes (§Output).
|
|
362
|
+
The roleless session-Captain is the sole signature exception: its public options-only `PlaybookRuntimeFactory<PlaybookRuntimeOptions>` wrapper supplies its fixed empty-ledger, fail-closed schema-3 capabilities internally because no Captain host exists above it.
|
|
363
|
+
|
|
364
|
+
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.
|
|
365
|
+
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.
|
|
366
|
+
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`, and additional typed ledger commands for the same atomic completion; and the ledger exposes its synchronous detached `snapshot(): PlaybookEffectLedger` mirror plus `writeAhead(commands: PlaybookEffectLedgerCommandBatch): Promise<PlaybookEffectLedger>` against the current host's atomic writer.
|
|
367
|
+
Only `configuredOptions` may reach option snapshotting and FSM input.
|
|
368
|
+
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.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
type XStateOutcomeFieldAuthority =
|
|
372
|
+
| 'presentation'
|
|
373
|
+
| 'semantic'
|
|
374
|
+
| 'effect'
|
|
375
|
+
| 'runtime';
|
|
376
|
+
|
|
377
|
+
type XStateRepositoryDisposition =
|
|
378
|
+
| 'unchanged'
|
|
379
|
+
| 'one-descendant-commit'
|
|
380
|
+
| 'deferred';
|
|
381
|
+
|
|
382
|
+
interface XStateGovernedOutcomeSpec {
|
|
383
|
+
readonly fields: Readonly<Record<string, XStateOutcomeFieldAuthority>>;
|
|
384
|
+
readonly repositoryDisposition: XStateRepositoryDisposition;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
interface XStateOutcomeAuthoritySpec {
|
|
388
|
+
readonly governedPlayerStates: Readonly<
|
|
389
|
+
Record<
|
|
390
|
+
string,
|
|
391
|
+
Readonly<Record<string, XStateGovernedOutcomeSpec>>
|
|
392
|
+
>
|
|
393
|
+
>;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
interface XStatePlaybookRuntimeConstruction<
|
|
397
|
+
ConfiguredOptions,
|
|
398
|
+
HostCapabilities extends object,
|
|
399
|
+
> {
|
|
400
|
+
readonly configuredOptions: ConfiguredOptions;
|
|
401
|
+
readonly hostCapabilities: HostCapabilities & {
|
|
402
|
+
readonly effectLedger: PlaybookEffectLedgerCapability;
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
The shared type-only contract module shall export `PlaybookRepositoryDisposition`, `PlaybookRepositoryObservation`, `PlaybookRepositoryReceipt`, `PlaybookEffectBoundary`, `PlaybookEffectBoundaryStart`, `PlaybookEffectLogicalOperation`, `PlaybookEffectLedger`, `PlaybookEffectLedgerCommand`, `PlaybookEffectLedgerCommandBatch`, and `PlaybookEffectLedgerCapability`; the executable `@sublang/playbook/xstate-runtime` module shall export `assertPlaybookEffectLedger`, `emptyPlaybookEffectLedger`, and `isPlaybookEffectLedgerMonotonicExtension` over those types.
|
|
408
|
+
That executable module shall also export the centralized schema-3 semantic surface: `PlaybookSemanticFieldAuthority`, `PlaybookSemanticOutcomeSpec`, `PlaybookSemanticEvidenceInput`, `PlaybookReconciledSemanticOutput`, `PlaybookRetainedSemanticEvidence`, `PlaybookSemanticReconciliationReason`, `PlaybookSemanticReconciliation`, `PlaybookSemanticCandidateStructureError`, and `reconcilePlaybookSemanticEvidence`.
|
|
409
|
+
The pure reconciler shall accept the declared state-local outcomes, an unknown semantic candidate, and optional unknown `finalText`, repository receipt, and runtime-field evidence; shall return a detached frozen `resolved` or `deferred` decision with exact output and retained evidence, or an `unresolved` decision with retained evidence and one closed reason from `missing-presentation-evidence`, `missing-repository-receipt`, `invalid-repository-receipt`, `repository-disposition-mismatch`, `missing-effect-evidence`, `missing-runtime-evidence`, and `inconsistent-runtime-evidence`; and shall reserve `PlaybookSemanticCandidateStructureError` for candidate defects eligible for the bounded correction path rather than effect-evidence disagreement.
|
|
410
|
+
The empty ledger shall be exactly `{ schemaVersion: 1, revision: 0, boundaries: [], logicalOperations: [] }`, and revision shall be zero if and only if both ordered ledgers are empty.
|
|
411
|
+
The validator shall capture the complete supplied ledger once as detached frozen JSON and enforce every closed member, identity, ordering, receipt, cross-reference, correction-budget, and logical-operation invariant represented above.
|
|
412
|
+
One optional host-owned UUID `cohortId` shall identify every member of exactly one contiguous, distinct-role, all-`unchanged` physical cohort in declared role order; every member shall share attempt, playbook, runtime-session, turn, canonical-worktree, and baseline identity and shall be uniformly started or uniformly complete, complete members shall carry the identical after observation and receipt, and the id shall never be reused by another group.
|
|
413
|
+
Within a logical operation, `checkpoint`, `pendingQuestion`, and `playerContinuation` shall be all present or all absent; the pending question shall preserve its exact nonempty authored identity and nonblank content, and `checkpointRestorationEligible: true` shall require that complete bound group.
|
|
414
|
+
Each logical operation shall reciprocally name every and only boundary carrying its operation id, share those boundaries' playbook and runtime-session identity, and use its first boundary's exact baseline as `originalBaseline`; every linked boundary shall use that baseline's canonical worktree and, after the first, start from the preceding boundary's complete after checkpoint, while a logical receipt shall require every linked physical receipt.
|
|
415
|
+
`isPlaybookEffectLedgerMonotonicExtension(checkpoint, current)` shall accept exact equality and only a ledger reachable through the typed append-or-replace transitions without boundary or operation deletion, identity or original-baseline reassignment, correction-budget replenishment, completed-receipt or evidence loss, or removal or reordering of an earlier boundary-id prefix. A spent correction may replace `semanticCandidate` exactly once only while adding `initialSemanticCandidate` equal to the prior candidate; that initial candidate then remains immutable. A replacement may append boundary ids and replace or clear the complete current checkpoint, pending-question, and player-continuation group together with its eligibility, while an existing logical receipt remains immutable ([DR-040](../specs/decisions/040-outcome-authority-effect-reconciliation.md)).
|
|
416
|
+
Every accepted non-idempotent command batch shall increment revision once; an exact start or append replay under the same boundary or operation identities and payload shall return the same acknowledged ledger, while conflicting identity reuse shall reject without mutation.
|
|
417
|
+
The host shall assign each started boundary's sequence, current uncertain-attempt UUID, and positive attempt number, and shall assign each appended logical operation's sequence.
|
|
418
|
+
Every command batch and every command's entry list shall be nonempty; the host shall apply its commands in order as one ledger transition, perform final cross-reference validation after the complete batch, and acknowledge only one atomic persistence and revision increment.
|
|
419
|
+
Every replace command's exact `{ expected, next }` pair shall compare-and-swap one present boundary or operation, preserve its identity and immutable fields, reject a stale expected value, and move optional evidence, the one-way correction budget, or the current logical binding and eligibility only as permitted by DR-040.
|
|
420
|
+
After a governed operation settles, the host shall retain any exact proposed completion batch only in live memory until it is acknowledged and the cooperative claim retires; an indeterminate same-process write shall retry or recognize that batch under the still-owned claim, while recovery after process death shall reconstruct only evidence provable from the durable baseline and current repository observation before source restoration.
|
|
421
|
+
|
|
422
|
+
`governedPlayerStates` shall name every delegated-player state declared by `roleStates`, or shall be exactly empty for an artifact with no delegated-player state; it shall name no other state.
|
|
423
|
+
Each state shall name exactly the outcomes in that state's `invoke.input.result`, and each outcome shall contain exactly `fields` and `repositoryDisposition`.
|
|
424
|
+
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.
|
|
425
|
+
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.
|
|
426
|
+
Each repository disposition shall be exactly `unchanged`, `one-descendant-commit`, or `deferred`; an effect-owned field is valid only on `one-descendant-commit`, and `deferred` is valid only on `needsBossReply` with presentation-owned `question` and another outcome in that state declaring `one-descendant-commit`.
|
|
427
|
+
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.
|
|
172
428
|
|
|
173
429
|
`init` receives the host-owned playbook session identity and ports, constructs the XState actor with FSM `input` derived from `options`, and starts the actor.
|
|
174
430
|
The runtime owns the actor for its lifetime; `handleBossInput` runs one turn, and `dispose` stops the actor and drains pending port emissions.
|
|
@@ -184,12 +440,14 @@ differ from both its `rootSessionId` and `parentSessionId`.
|
|
|
184
440
|
Run outcomes are exact: `no-action` means no FSM event was sent;
|
|
185
441
|
`quiescent` means a non-failure parked/idle state; `failed` means the FSM is in
|
|
186
442
|
a recoverable failure state; `terminal` means top-level final with optional
|
|
187
|
-
JSON output
|
|
443
|
+
JSON output and the exact authored `stateDescription` of the reached final
|
|
444
|
+
state when one is declared; `aborted` means the turn signal ended work; and `suspended` means
|
|
188
445
|
exactly one `pendingCall` is active.
|
|
446
|
+
Only the terminal variant may carry `stateDescription`; the runtime shall omit it when the final state declares none and shall never substitute a state id or derive it from opaque output ([DR-037](../specs/decisions/037-terminal-result-meaning.md)).
|
|
189
447
|
Control-plane exceptions reject the runtime method rather than masquerade as a
|
|
190
448
|
recoverable workflow `failed` result.
|
|
191
449
|
|
|
192
|
-
|
|
450
|
+
Configured options shall be plain JSON and live host seams shall enter only through `hostCapabilities` in the disjoint schema-3 construction input above.
|
|
193
451
|
The link compiler emits a typed options interface per playbook based on the FSM's `CodingInput` (or equivalent).
|
|
194
452
|
The CLI's absence of `--link-option` values does not mean that
|
|
195
453
|
`PlaybookRuntimeOptions` is empty. CLI link options are compile-time inputs;
|
|
@@ -285,6 +543,8 @@ type PlaybookCallStart =
|
|
|
285
543
|
| { state: 'suspended'; childSessionId: string };
|
|
286
544
|
```
|
|
287
545
|
|
|
546
|
+
A runtime's `{ outcome: 'unresolved-effect', state }` abandonment result is not a `PlaybookCallResult`: a host shall not translate it into child output or error, resume a parent FSM with it, or treat it as authored completion.
|
|
547
|
+
|
|
288
548
|
`PlayerResult` mirrors the status, resume token, final text, and error fields of cligent's `PlayerRunResult` ([TMUX-033](https://github.com/sublang-ai/cligent/blob/main/specs/user/tmux-play.md#tmux-033)).
|
|
289
549
|
The runtime treats `status !== 'ok'` as a player failure and routes it through the FSM's error path (§Abort).
|
|
290
550
|
An `ok` player result whose `finalText` is missing, empty, or whitespace-only
|
|
@@ -386,7 +646,7 @@ The runtime never speaks to LLMs directly and never touches host types beyond `P
|
|
|
386
646
|
## Playbook trace
|
|
387
647
|
|
|
388
648
|
Every linked runtime shall emit a boundary-complete, ordered trace through `emitTelemetry` topic `playbook.trace`.
|
|
389
|
-
Each payload shall carry `schemaVersion:
|
|
649
|
+
Each payload shall carry `schemaVersion: 4`, the immutable session identity and
|
|
390
650
|
causality, a contiguous one-based `sequence`, a Unix-millisecond `timestamp`, a
|
|
391
651
|
trace `type`, event `payload`, and the runtime-local `turnId` / paired `callId`
|
|
392
652
|
where applicable.
|
|
@@ -406,12 +666,13 @@ type PlaybookTraceType =
|
|
|
406
666
|
| 'apply.started'
|
|
407
667
|
| 'apply.finished'
|
|
408
668
|
| 'fsm.transition'
|
|
669
|
+
| 'outcome.accepted'
|
|
409
670
|
| 'status.emitted'
|
|
410
671
|
| 'boss.input.settled'
|
|
411
672
|
| 'session.disposed';
|
|
412
673
|
|
|
413
674
|
interface PlaybookTraceEvent {
|
|
414
|
-
schemaVersion:
|
|
675
|
+
schemaVersion: 4;
|
|
415
676
|
sessionId: string;
|
|
416
677
|
playbookId: string;
|
|
417
678
|
rootSessionId: string;
|
|
@@ -437,8 +698,9 @@ The trace types are `session.started`, `boss.input.received`,
|
|
|
437
698
|
`player.call.finished`, `captain.call.started`, `captain.call.finished`,
|
|
438
699
|
`playbook.call.started`,
|
|
439
700
|
`playbook.call.finished`, `apply.started`, `apply.finished`,
|
|
440
|
-
`fsm.transition`, `status.emitted`,
|
|
701
|
+
`fsm.transition`, `outcome.accepted`, `status.emitted`,
|
|
441
702
|
`boss.input.settled`, and `session.disposed`.
|
|
703
|
+
No trace event whose `schemaVersion` is below `4` is authority-bearing accepted-outcome evidence.
|
|
442
704
|
Call pairs carry exact prompts and replies, normalized failures, actor and state
|
|
443
705
|
identity, and their boundary-specific options.
|
|
444
706
|
`apply.started` and `apply.finished` are the paired apply boundary of a
|
|
@@ -453,8 +715,13 @@ Direct-Captain start and finish payloads shall carry `allowedTools` exactly when
|
|
|
453
715
|
the originating `CaptainCallOptions` selects it and shall omit the member when
|
|
454
716
|
the call preserves the host Captain's configured tools.
|
|
455
717
|
`session.started` and `session.disposed` carry their descriptor as top-level
|
|
456
|
-
`state` and its singular `stateId` when present.
|
|
457
|
-
|
|
718
|
+
`state` and its singular `stateId` when present. An adopted runtime begins its
|
|
719
|
+
fresh target trace with `session.started` at sequence `1`; that event also
|
|
720
|
+
carries an exact nested `adoption` object with `sourceSessionId` and
|
|
721
|
+
`sourceGenerationId`, plus the source/target call and child-session identities
|
|
722
|
+
when a suspended edge was rebased. The suspended form carries the fresh target
|
|
723
|
+
call id as top-level `callId` and carries no source `turnId`. Every judge start
|
|
724
|
+
and finish carries the working snapshot's singular `stateId` when one exists;
|
|
458
725
|
classification uses the current descriptor and adjudication uses the invoking
|
|
459
726
|
actor input. The default Captain always has such a singular id, while a
|
|
460
727
|
parallel snapshot may omit it. Every judge finish also carries
|
|
@@ -494,6 +761,15 @@ If a started-boundary sink records the event and then rejects, the runtime
|
|
|
494
761
|
shall make one best-effort normalized error-finish attempt with the same call
|
|
495
762
|
id and then reject the original start error. It shall not retry either event or
|
|
496
763
|
let a failure of that finish attempt replace the start error.
|
|
764
|
+
A start-sink rejection causally identical to the applicable signal reason is
|
|
765
|
+
the cancellation itself, not a control error: no host call begins, the
|
|
766
|
+
best-effort paired finish carries the boundary's canonical aborted evidence —
|
|
767
|
+
`status: 'aborted'` for a host call, or the rejected-before-effect disposition
|
|
768
|
+
and reason for apply — and nothing is latched. An ordinary run boundary settles
|
|
769
|
+
as §Abort prescribes. At the apply boundary the same event remains
|
|
770
|
+
pre-acceptance: `apply` rejects with that exact reason, records no receipt, and
|
|
771
|
+
leaves the key reusable
|
|
772
|
+
([DR-036](../specs/decisions/036-coherent-abort-settlement.md)).
|
|
497
773
|
When a call boundary carries `callId`, that id shall be unique within the
|
|
498
774
|
runtime session. A stable FSM `stateId` is identity metadata in the payload,
|
|
499
775
|
not a call id and shall not be reused as one across repeated invocations.
|
|
@@ -506,6 +782,7 @@ result: its outcome must be one of the `PlaybookRunResult` discriminants (never
|
|
|
506
782
|
an invented `error` outcome), and it shall include `state`, singular
|
|
507
783
|
`stateId`, `pendingCall`, `output`, and normalized `error` whenever the matching
|
|
508
784
|
result arm carries them.
|
|
785
|
+
The `unresolved-effect` arm shall carry only `state`; bounded repository-effect evidence remains host-owned and shall enter neither that result nor its trace projection.
|
|
509
786
|
One runtime-owned concurrency-one emission queue shall serialize every trace,
|
|
510
787
|
human status, and state telemetry call. Sequence allocation and enqueueing
|
|
511
788
|
shall occur atomically, and every public method shall drain that queue before
|
|
@@ -658,10 +935,14 @@ nonconformant.
|
|
|
658
935
|
The FSM's `events` union enumerates every Boss-originated event.
|
|
659
936
|
The runtime receives Boss input as a free-form string
|
|
660
937
|
(`handleBossInput.text`).
|
|
661
|
-
Where the current ready or reconstructed
|
|
938
|
+
Where the current ready, recoverable-failure (`failed`), or reconstructed
|
|
939
|
+
terminal machine accepts exactly one
|
|
662
940
|
ordinary textual entry event and no Boss question is pending, the runtime
|
|
663
941
|
shall send that event deterministically and attach the exact original text to
|
|
664
|
-
its declared textual payload field without invoking `callJudge
|
|
942
|
+
its declared textual payload field without invoking `callJudge`: each of the
|
|
943
|
+
three is an entry awaiting a fresh intent, so delivered text has exactly one
|
|
944
|
+
meaning there and a judge call could only spend budget or settle the restart
|
|
945
|
+
as no action.
|
|
665
946
|
The default Captain — the controller playbook of
|
|
666
947
|
[gears2fsm "Setup"](gears2fsm.md#setup) — is deterministic at every parked
|
|
667
948
|
entry: the runtime maps each Boss turn from the exact text and the host's
|
|
@@ -771,7 +1052,8 @@ clause (or equivalent typed output metadata). Backticked prose before that
|
|
|
771
1052
|
clause can name statuses, guards, or concepts such as `ok`, `aborted`, and
|
|
772
1053
|
`error`; those names are not output properties and shall never become required
|
|
773
1054
|
judge fields.
|
|
774
|
-
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.
|
|
1055
|
+
For a nongoverned 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.
|
|
1056
|
+
For an artifact-schema-3 governed field, that annotation instead declares presentation authority and any judge-supplied value is a structural error under the authority rule below.
|
|
775
1057
|
The linker shall derive the complete `verbatimPayloadFields` set from those annotations across the FSM result maps.
|
|
776
1058
|
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.
|
|
777
1059
|
For a direct Captain result, `question` and `response` are human-presentation
|
|
@@ -784,13 +1066,87 @@ After validating that selection, the runtime shall inject the exact non-empty
|
|
|
784
1066
|
It shall reject a judge reply that supplies either presentation field as an
|
|
785
1067
|
undeclared extra key, so hidden adjudication cannot replace, paraphrase, or
|
|
786
1068
|
decorate prose Boss already saw.
|
|
787
|
-
|
|
788
|
-
|
|
1069
|
+
For an artifact-schema-3 governed delegated-player call, the shared engine
|
|
1070
|
+
shall instead use one semantic reconciler for both the default linked runtime
|
|
1071
|
+
and any bespoke linked runtime that adopts schema `3`.
|
|
1072
|
+
That reconciler shall retain the validated player's exact non-empty
|
|
1073
|
+
`finalText` as opaque presentation evidence, let the hidden adjudicator read
|
|
1074
|
+
it only as semantic evidence, and require the adjudicator's detached
|
|
1075
|
+
plain-JSON candidate to contain exactly `guard` plus every and only
|
|
1076
|
+
semantic-owned payload field declared for that guard.
|
|
1077
|
+
The candidate shall therefore contain no presentation-, effect-, or
|
|
1078
|
+
runtime-owned payload field; `guard` shall name exactly one outcome declared
|
|
1079
|
+
by both the live result map and `outcomeAuthority`; and every semantic-owned
|
|
1080
|
+
field shall satisfy the result map's required-field type before any actor
|
|
1081
|
+
output is delivered.
|
|
1082
|
+
The reconciler shall construct the complete actor output rather than accept a
|
|
1083
|
+
cross-authority object from the judge: every presentation-owned payload field
|
|
1084
|
+
shall receive the canonical `finalText.trim()` value, effect-owned
|
|
1085
|
+
`latestCommit` shall receive only the qualifying receipt's exact commit OID,
|
|
1086
|
+
and no authority may supply, overwrite, or contradict another authority's
|
|
1087
|
+
field.
|
|
1088
|
+
It shall reject an absent required field, an undeclared or extra field, a
|
|
1089
|
+
field supplied by the wrong authority, an invalid value, or any mutually
|
|
1090
|
+
inconsistent candidate before FSM delivery.
|
|
1091
|
+
|
|
1092
|
+
For a non-deferred candidate, reconciliation shall require a complete durable
|
|
1093
|
+
physical receipt, or the complete cumulative logical receipt of a deferred
|
|
1094
|
+
operation, whose classification is exactly the outcome's declared
|
|
1095
|
+
`unchanged` or `one-descendant-commit` disposition; the latter shall carry
|
|
1096
|
+
exactly the after-HEAD OID used for `latestCommit`.
|
|
1097
|
+
A `deferred` candidate shall be admissible only for its already-validated
|
|
1098
|
+
effect-authorized `needsBossReply` outcome and only from a complete after
|
|
1099
|
+
observation whose HEAD equals the logical operation's original baseline HEAD
|
|
1100
|
+
and whose classification is exactly `unchanged` or `worktree-only-change`.
|
|
1101
|
+
It shall become deliverable only after the current host durably acknowledges
|
|
1102
|
+
the exact checkpoint, question, continuation, and logical-operation binding
|
|
1103
|
+
defined above; a missing checkpoint, changed HEAD, multiple or rewritten
|
|
1104
|
+
history, detected concurrent or foreign change, or ambiguous observation
|
|
1105
|
+
shall leave it unresolved.
|
|
1106
|
+
|
|
1107
|
+
The first structurally invalid schema-3 semantic reply shall make at most one
|
|
1108
|
+
corrective hidden adjudication eligible over the identical retained
|
|
1109
|
+
presentation evidence and declared outcome schema, with the validation error
|
|
1110
|
+
restated.
|
|
1111
|
+
Before starting that corrective judge, the runtime shall compare-and-swap the
|
|
1112
|
+
boundary's `correctionBudget` from `{ limit: 1, spent: false }` to
|
|
1113
|
+
`{ limit: 1, spent: true }` while atomically retaining its receipt, opaque
|
|
1114
|
+
presentation, and first recoverable invalid `semanticCandidate` through
|
|
1115
|
+
`effectLedger.writeAhead`, await the durable acknowledgement, replace its
|
|
1116
|
+
mirror with that acknowledged ledger, and check the applicable abort signal
|
|
1117
|
+
again.
|
|
1118
|
+
A failed or indeterminate spend, an acknowledgement that does not contain the
|
|
1119
|
+
exact one-way update, a previously spent budget, or an abort before the call
|
|
1120
|
+
begins shall start no corrective judge; the spent value shall remain spent
|
|
1121
|
+
across export, restore, adoption, and process restart.
|
|
1122
|
+
A second structurally invalid reply shall receive no further correction.
|
|
1123
|
+
A player abort, error, non-`ok` result, or missing non-empty `finalText` shall
|
|
1124
|
+
start no adjudication, while an initial or corrective judge transport failure
|
|
1125
|
+
or invalid host result shall start no corrective or third judge respectively.
|
|
1126
|
+
|
|
1127
|
+
Only a complete, authority-consistent semantic-and-effect envelope shall be
|
|
1128
|
+
delivered once to the FSM, and only after its evidence and applicable
|
|
1129
|
+
correction-budget or deferred-operation updates are durably acknowledged.
|
|
1130
|
+
The completion path shall retain the opaque `finalText`, the latest recoverable
|
|
1131
|
+
detached plain-JSON semantic candidate even when it is structurally invalid,
|
|
1132
|
+
and the receipt and correction budget without parsing the presentation for a
|
|
1133
|
+
repository fact; where a correction replaces that candidate, immutable
|
|
1134
|
+
`initialSemanticCandidate` shall preserve the candidate that consumed the
|
|
1135
|
+
budget, while a malformed reply from which no JSON value can be recovered may
|
|
1136
|
+
omit both candidates.
|
|
1137
|
+
An effect-possible envelope whose presentation, semantic, effect, or deferred
|
|
1138
|
+
checkpoint evidence is absent, invalid, incomplete, or inconsistent shall
|
|
1139
|
+
deliver no actor output and shall remain parked for later reconciliation;
|
|
1140
|
+
once its matching source state is restored, reconstruction from a durable
|
|
1141
|
+
complete envelope may perform that same reconciliation once without another
|
|
1142
|
+
player or judge call.
|
|
789
1143
|
The adjudicator shall use the same document-order tolerant JSON recovery as
|
|
790
1144
|
the Boss classifier. Unlike invalid classification, a reply from which no
|
|
791
1145
|
object can be recovered, an undeclared guard, or a missing required field is a
|
|
792
|
-
control-plane error
|
|
793
|
-
path and ordered
|
|
1146
|
+
control-plane error for nongoverned delegated-player and direct-Captain adjudication and
|
|
1147
|
+
shall throw after the invocation reaches its FSM error path and ordered
|
|
1148
|
+
emissions drain; schema-3 governed adjudication follows the bounded
|
|
1149
|
+
reconciliation contract above instead.
|
|
794
1150
|
|
|
795
1151
|
Two default adjudication strategies, in selection order:
|
|
796
1152
|
|
|
@@ -798,9 +1154,11 @@ Two default adjudication strategies, in selection order:
|
|
|
798
1154
|
names the source item's actor (and delegated player where applicable),
|
|
799
1155
|
includes the actor's verbatim output,
|
|
800
1156
|
lists the `result` keys with their descriptions, and demands a JSON
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
`
|
|
1157
|
+
answer keyed to exactly one of the declared guards: a nongoverned player uses
|
|
1158
|
+
`{ guard, …structuralPayloadFields }`, while a governed schema-3 player uses
|
|
1159
|
+
`{ guard, …semanticOwnedPayloadFields }` and explicitly forbids every other
|
|
1160
|
+
payload field. Both forms exclude the runtime-injected direct-Captain
|
|
1161
|
+
`question` and `response` fields above. The prompt shall identify hidden control work,
|
|
804
1162
|
prohibit tool use, file inspection, and external evidence, direct the judge
|
|
805
1163
|
to decide only from the supplied actor output and declared outcomes, and
|
|
806
1164
|
require exactly one JSON object with no prose. The judge prompt shall not
|
|
@@ -873,7 +1231,7 @@ the `{ visibility: 'visible', resume: false }` workflow-call selection
|
|
|
873
1231
|
(§PlaybookPorts contract, §Captain prompt composition) stay the
|
|
874
1232
|
visible-presentation shape for non-controller playbooks.
|
|
875
1233
|
|
|
876
|
-
The adjudicator shall fail loudly on:
|
|
1234
|
+
The nongoverned player adjudicator and every direct-Captain adjudicator shall fail loudly on:
|
|
877
1235
|
|
|
878
1236
|
- A guard the state does not declare,
|
|
879
1237
|
- A missing payload field the state's `result` description requires,
|
|
@@ -885,7 +1243,7 @@ identify an undeclared guard, and an incomplete selection shall identify the
|
|
|
885
1243
|
missing required field. A generic “no declared guard selected” error for all
|
|
886
1244
|
three cases is nonconformant.
|
|
887
1245
|
|
|
888
|
-
|
|
1246
|
+
Those adjudicator failures are control-plane errors.
|
|
889
1247
|
The runtime shall propagate them by throwing out of `handleBossInput` after attempting cleanup.
|
|
890
1248
|
The host adapter surfaces the throw on its control-plane channel (cligent surfaces such throws as `runtime_error` per [TMUX-025](https://github.com/sublang-ai/cligent/blob/main/specs/user/tmux-play.md#tmux-025)).
|
|
891
1249
|
The host's player-result channels (`player_finished` and equivalents) are reserved for failures the player itself produced; the host emits them when `callPlayer` resolves with `status !== 'ok'`.
|
|
@@ -895,11 +1253,12 @@ failure path specified above. Captain transport, result-shape, trace-sink, and
|
|
|
895
1253
|
adjudication failures remain control-plane errors unless the transport failure
|
|
896
1254
|
is causally identical to the active abort signal.
|
|
897
1255
|
Because XState still needs the invoked promise to settle, the linked runtime
|
|
898
|
-
shall latch
|
|
1256
|
+
shall latch a nongoverned delegated-player or direct-Captain adjudicator failure, actor-output JSON-validation, or nested-boundary
|
|
899
1257
|
control error outside machine context, allow the invocation's `onError` path to
|
|
900
1258
|
reach quiescence, drain all emissions, and then reject the public runtime
|
|
901
1259
|
method with that original error. It shall not return such a failure as a
|
|
902
1260
|
recoverable `{ outcome: 'failed' }` workflow result.
|
|
1261
|
+
An artifact-schema-3 governed-player adjudicator shall instead use the bounded structural correction, authority reconciliation, and unresolved parking contract above.
|
|
903
1262
|
The first latched non-abort control error takes precedence over a coincident
|
|
904
1263
|
boundary-signal abort. Read and clear the latch only in the public boundary's
|
|
905
1264
|
`finally` cleanup after XState and emissions have settled, so it cannot leak
|
|
@@ -937,10 +1296,34 @@ The provided actor shall:
|
|
|
937
1296
|
`{ guard: <first declared guard>, exitStatus: 0 }`; any nonzero status
|
|
938
1297
|
resolves the second declared guard with that status. Guard selection is
|
|
939
1298
|
mechanical; the runtime shall not route script output through the judge.
|
|
940
|
-
- Reject
|
|
941
|
-
state's ordinary `onError` path.
|
|
942
|
-
|
|
943
|
-
|
|
1299
|
+
- Reject when the command cannot be spawned at all, routing through the
|
|
1300
|
+
state's ordinary `onError` path. Beyond spawn failure, the invocation
|
|
1301
|
+
rejects only per the abort bullet below or when one of its own script
|
|
1302
|
+
emissions rejects; a completed command's exit status itself never rejects.
|
|
1303
|
+
- Honor the active turn's abort signal per §Abort: the actor shall reject
|
|
1304
|
+
without spawning when the combined signal is already aborted; shall run the
|
|
1305
|
+
shell detached as its own process-group leader; and on abort — whenever it
|
|
1306
|
+
lands before the invocation settles, including only after the shell's own
|
|
1307
|
+
exit — shall deliver
|
|
1308
|
+
`SIGTERM` to the entire group, escalate to `SIGKILL` after a bounded grace,
|
|
1309
|
+
and settle only after the shell process itself has exited and the group has
|
|
1310
|
+
stopped being signalable, confirmed by an `ESRCH` liveness probe, rejecting
|
|
1311
|
+
with the signal's reason. The same
|
|
1312
|
+
bounded grace caps the post-`SIGKILL` wait for kernel teardown, so an
|
|
1313
|
+
unreaped member outside the runtime's control cannot stall settlement. If
|
|
1314
|
+
the group remains signalable through that bound, or confirmation fails
|
|
1315
|
+
without `ESRCH`, the boundary rejects with a distinct teardown control error
|
|
1316
|
+
rather than reporting a clean abort over unconfirmed cleanup. The kill is
|
|
1317
|
+
always posted before the actor settles. Abort ownership — the
|
|
1318
|
+
listener and its escalation — spans the whole invocation, not the
|
|
1319
|
+
spawn-to-exit window
|
|
1320
|
+
([DR-036](../specs/decisions/036-coherent-abort-settlement.md)). An abort
|
|
1321
|
+
observed only after the shell's exit shall additionally reject before guard
|
|
1322
|
+
resolution and before starting any script emission not already in flight; an
|
|
1323
|
+
emission already started when the abort lands completes through the
|
|
1324
|
+
ordinary serialized channel and the rejection follows it. A
|
|
1325
|
+
descendant that leaves the process group is beyond the runtime's kill
|
|
1326
|
+
scope.
|
|
944
1327
|
- Emit, after the child settles and before the invocation resolves, one status
|
|
945
1328
|
line `Executed script for <stateId> (exit <status>).` and one telemetry
|
|
946
1329
|
event under topic `playbook.script` with payload
|
|
@@ -965,14 +1348,18 @@ XState `.provide(...)` receives the exact declared actor input rather than a
|
|
|
965
1348
|
structurally similar local type.
|
|
966
1349
|
Construct one bridge per runtime and wire every integration hook: allocate ids
|
|
967
1350
|
with `nextCallId`; return the currently active public-boundary signal from
|
|
968
|
-
`getBoundarySignal`;
|
|
969
|
-
|
|
970
|
-
through `
|
|
971
|
-
`drain`;
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1351
|
+
`getBoundarySignal`; capture an immutable cancellation classifier for the
|
|
1352
|
+
invocation's signal identities; compose `resumePlaybookCall.signal` into that
|
|
1353
|
+
classifier through `bindResumeSignal`; pass the applicable classifier through
|
|
1354
|
+
`emitStarted`, `emitFinished`, and `drain`; bind it to the root transition
|
|
1355
|
+
caused by child settlement through `bindActorSettlement`; and pass it through
|
|
1356
|
+
`onControlPlaneError` and `onBackgroundError`. Each receiving latch shall drop
|
|
1357
|
+
only a failure the supplied classifier identifies as exact cancellation and
|
|
1358
|
+
shall retain every distinct cleanup or observer failure for the owning public
|
|
1359
|
+
boundary, the next drain, or disposal rejection as applicable. A stored
|
|
1360
|
+
distinct failure shall never be reclassified against a later boundary. The
|
|
1361
|
+
runtime shall not leave these optional API hooks unwired merely because their
|
|
1362
|
+
TypeScript properties are optional for simpler bridge consumers.
|
|
976
1363
|
On invocation the bridge allocates a runtime-local call id, traces the start,
|
|
977
1364
|
and calls `PlaybookPorts.callPlaybook` with the composed target/text and the
|
|
978
1365
|
bridge signal combined from the XState invocation lifetime, the active public
|
|
@@ -1057,12 +1444,15 @@ registry; linker-time metadata is not authorization to call a target.
|
|
|
1057
1444
|
|
|
1058
1445
|
Disposal shall settle an outstanding call as aborted and drain its finish
|
|
1059
1446
|
trace before `session.disposed`.
|
|
1060
|
-
If registered child abort cleanup rejects
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1447
|
+
If registered child abort cleanup rejects with a failure distinct from every
|
|
1448
|
+
applicable abort reason, the bridge shall emit the paired finish with an error
|
|
1449
|
+
result and reject `abortPending` or disposal with that original cleanup error,
|
|
1450
|
+
or with an aggregate containing every distinct failure when more than one
|
|
1451
|
+
remains;
|
|
1452
|
+
an exact abort-reason rejection is cancellation evidence and shall not be
|
|
1453
|
+
retained as a control failure. Parent disposal shall still drain, emit its one
|
|
1454
|
+
`session.disposed` boundary, and clear the bound session before rejecting with
|
|
1455
|
+
any preserved distinct cleanup error.
|
|
1066
1456
|
Child output and errors must be JSON-safe; a non-JSON-safe result is a
|
|
1067
1457
|
control-plane error.
|
|
1068
1458
|
|
|
@@ -1140,6 +1530,12 @@ The `PlaybookRuntime` shall:
|
|
|
1140
1530
|
transition-trace or telemetry sink failure is part of `init`: initialization
|
|
1141
1531
|
shall reject, stop the actor, and perform the failed-start cleanup below
|
|
1142
1532
|
rather than swallowing it as a later background error.
|
|
1533
|
+
A root-actor error observed during startup — an initial entry action or a
|
|
1534
|
+
synchronously failing initial invocation — is equally part of `init` and
|
|
1535
|
+
`restore`: the boundary shall reject with that original error after the
|
|
1536
|
+
failed-start cleanup, and shall never resolve leaving the errored actor as
|
|
1537
|
+
later background state
|
|
1538
|
+
([DR-036](../specs/decisions/036-coherent-abort-settlement.md)).
|
|
1143
1539
|
Where the FSM input declares `selfPlaybookId`, seed it from the immutable
|
|
1144
1540
|
`session.playbookId`; do not expose a caller option or reuse a working leaf's
|
|
1145
1541
|
`stateId` as the self-call identity.
|
|
@@ -1193,7 +1589,14 @@ The `PlaybookRuntime` shall:
|
|
|
1193
1589
|
transition/status/telemetry queue before returning, just as
|
|
1194
1590
|
`handleBossInput` does. A resume shall not allocate a new Boss-input
|
|
1195
1591
|
`turnId`; retain the original call-start turn id for its matching finish and
|
|
1196
|
-
for the parent continuation caused by that return.
|
|
1592
|
+
for the parent continuation caused by that return.
|
|
1593
|
+
A resume whose signal is already aborted after identity and result
|
|
1594
|
+
validation shall deliver nothing: bind no resume signal, settle no deferred,
|
|
1595
|
+
emit no call finish, and preserve the pending call — the boundary settles
|
|
1596
|
+
`{ outcome: 'aborted' }` with the signal's reason while the suspended state
|
|
1597
|
+
and pending identity survive, so a later resume with the same call id and a
|
|
1598
|
+
fresh signal still delivers
|
|
1599
|
+
([DR-036](../specs/decisions/036-coherent-abort-settlement.md)). Every success and
|
|
1197
1600
|
exceptional path shall drain ordered emissions, select the first latched
|
|
1198
1601
|
non-abort control error before considering abort, and clear its boundary
|
|
1199
1602
|
latches in `finally`, so a failed resume cannot leak an emission error into a
|
|
@@ -1219,18 +1622,22 @@ The `PlaybookRuntime` shall:
|
|
|
1219
1622
|
The actor's `lastError` field shall be surfaced via `emitStatus` when the machine enters its `failed` state.
|
|
1220
1623
|
Presence of linker-emitted `roleStates` selects the canonical factory-backed status profile.
|
|
1221
1624
|
That profile shall emit the selected Boss event type
|
|
1222
|
-
before sending that event
|
|
1223
|
-
rider)
|
|
1625
|
+
before sending that event; exactly `→ <acceptedOutcome>` (with no payload-count or tally
|
|
1626
|
+
rider) only from a confirmed accepted-outcome marker; and
|
|
1224
1627
|
`⤷ <Role>: <label>` only when the entered state appears in the linked module's `roleStates` metadata.
|
|
1225
1628
|
It shall emit no raw state-id fallback for any other state.
|
|
1226
1629
|
`roleStates` shall be a complete map of the FSM states
|
|
1227
|
-
that invoke the typed `player` actor; each
|
|
1630
|
+
that invoke the typed `player` actor; each value carries the exact
|
|
1228
1631
|
local role from that state's source-derived `meta.playbook.role` and the state's exact FSM description as `{ role, label }`.
|
|
1229
1632
|
The factory shall reject an
|
|
1230
1633
|
incomplete entry, a non-player state, or a role or label that differs from the FSM metadata.
|
|
1231
1634
|
Artifact schema `1` and a missing compatibility declaration
|
|
1232
1635
|
shall reject before interpretation because their legacy `player` values may
|
|
1233
1636
|
encode bindings or aliases rather than canonical local roles.
|
|
1637
|
+
For artifact schema `3`, an accepted-outcome marker is a root-machine XState action with exact type `playbook.acceptedOutcome` and exact plain-data params `{ source, target, acceptedOutcome }` naming a declared governed outcome.
|
|
1638
|
+
The runtime shall observe that action only through the public root `@xstate.action` inspection event, retain it privately until the corresponding next public root `@xstate.snapshot` confirms `source` active in the prior snapshot and `target` active in the new snapshot, then emit one trace-schema-4 `outcome.accepted` event with those exact params before its canonical status and before public settlement; markers confirmed together shall retain their XState execution order.
|
|
1639
|
+
A valid unmarked transition, including an unexecuted guarded arm or rejected-guard fallback, shall settle normally with neither accepted-outcome evidence nor claimed-outcome status.
|
|
1640
|
+
An executed marker that is malformed, undeclared, or unconfirmed by those adjacent snapshots, or a batch that instruments one governed source more than once regardless of target or outcome, shall clear the entire pending marker batch and fail the current public boundary after retaining the ordinary transitioned state but before settlement, accepted-outcome evidence, or claimed-outcome status.
|
|
1234
1641
|
For the default Captain runtime, an initial `ready` state and a terminal `done`
|
|
1235
1642
|
state shall not emit human status. The terminal response is already visible
|
|
1236
1643
|
Captain prose; a synthetic “entered done” message would present it twice.
|
|
@@ -1246,9 +1653,14 @@ transition first.
|
|
|
1246
1653
|
|
|
1247
1654
|
If a `*.call.started` trace records and then its sink rejects, no host call may
|
|
1248
1655
|
begin. The runtime shall still enqueue exactly one synthetic paired
|
|
1249
|
-
`*.call.finished` trace with `status: 'error'`,
|
|
1656
|
+
`*.call.finished` trace — with `status: 'error'`, or `status: 'aborted'` when
|
|
1657
|
+
the sink rejection is causally identical to the applicable signal reason, in
|
|
1658
|
+
which case nothing is latched and the turn follows abort settlement
|
|
1659
|
+
([DR-036](../specs/decisions/036-coherent-abort-settlement.md)) — preserving
|
|
1660
|
+
the original call
|
|
1250
1661
|
id, turn id, actor visibility, state/source identity, and prompt or request
|
|
1251
|
-
metadata from the start boundary.
|
|
1662
|
+
metadata from the start boundary. A distinct rejection shall then follow the
|
|
1663
|
+
same latched
|
|
1252
1664
|
control-error, FSM settlement, and ordered-drain path as any other call-start
|
|
1253
1665
|
failure; the synthetic finish must not replace the original sink error.
|
|
1254
1666
|
|
|
@@ -1268,18 +1680,22 @@ quiescent state with actor status `active`.
|
|
|
1268
1680
|
At a safe capture point it shall return a JSON-safe
|
|
1269
1681
|
`PlaybookRuntimeSnapshot` carrying:
|
|
1270
1682
|
|
|
1271
|
-
- `schemaVersion`: literal `
|
|
1683
|
+
- `schemaVersion`: literal `4`.
|
|
1272
1684
|
- `playbookId`: the bound session's playbook id.
|
|
1273
1685
|
- `machine`: the root actor's `getPersistedSnapshot()` result, passed
|
|
1274
1686
|
through the shared JSON detachment with any raw `Error` context value
|
|
1275
1687
|
(for example FSM `lastError`) normalized to `{ name, message, stack? }`
|
|
1276
1688
|
first. The value is opaque to hosts.
|
|
1689
|
+
- `effectLedger`: the detached immutable schema-version-1 mirror most recently
|
|
1690
|
+
acknowledged by the current host's atomic ledger channel; a linked workflow
|
|
1691
|
+
runtime carries the complete current-host mirror, while the internal compiled
|
|
1692
|
+
Captain runtime carries the exact empty ledger.
|
|
1277
1693
|
- `roleResumeTokens`: the local-role resume-token projection as a plain object
|
|
1278
1694
|
(§PlaybookPorts contract).
|
|
1279
1695
|
- `sequences`: the live `trace`, `turn`, `judgeCall`, `playerCall`, and
|
|
1280
1696
|
`playbookCall` counters, plus `captainCall` when the runtime supports direct
|
|
1281
1697
|
Captain calls.
|
|
1282
|
-
A direct-Captain-capable runtime shall persist it in every schema-version-
|
|
1698
|
+
A direct-Captain-capable runtime shall persist it in every schema-version-4 export.
|
|
1283
1699
|
- `state`: the current normalized state descriptor.
|
|
1284
1700
|
- `pendingBossQuestions`: the pending Boss question(s) from FSM context as
|
|
1285
1701
|
a list of `{ questionId, asker, question, sourceItem? }`, where `asker` is
|
|
@@ -1301,14 +1717,18 @@ unsafe and returns `undefined`.
|
|
|
1301
1717
|
`restore(session, snapshot)` is an alternative to `init` under the same
|
|
1302
1718
|
lifecycle guards (§Session lifecycle): it shall reject when already
|
|
1303
1719
|
initialized, disposing, or disposed, and shall validate
|
|
1304
|
-
schema version `
|
|
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.
|
|
1720
|
+
schema version `4`, the complete effect-ledger mirror, and that `snapshot.playbookId` equals `session.playbookId` before touching state.
|
|
1721
|
+
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; schema `3` shall reject because it cannot prove an effect ledger.
|
|
1306
1722
|
The host supplies the same immutable `PlaybookSession` identity the
|
|
1307
1723
|
snapshot was exported under and recreates the runtime through the same
|
|
1308
1724
|
factory with equivalent options; the runtime does not diff options, and
|
|
1309
1725
|
module identity — that the factory constructing this runtime still
|
|
1310
1726
|
belongs to the snapshot's playbook — is likewise the host's check to
|
|
1311
1727
|
make before calling `restore`.
|
|
1728
|
+
Before actor or source-state restoration, a linked workflow runtime shall require
|
|
1729
|
+
the snapshot ledger to equal the detached synchronous mirror exposed by its
|
|
1730
|
+
current-host capability; the internal Captain runtime shall require its mirror
|
|
1731
|
+
to be empty.
|
|
1312
1732
|
`restore` shall bind the session and its current detached role bindings, restore the local-role token projection, the
|
|
1313
1733
|
sequence counters, and the
|
|
1314
1734
|
prior-state descriptor from the snapshot,
|
|
@@ -1331,6 +1751,110 @@ ownership without a child-host call or duplicate start/finish boundary.
|
|
|
1331
1751
|
A restore failure shall leave the runtime unbound so `dispose` remains
|
|
1332
1752
|
callable and terminal.
|
|
1333
1753
|
|
|
1754
|
+
## Retained-snapshot adoption (optional)
|
|
1755
|
+
|
|
1756
|
+
A linked runtime may implement the optional adoption capability of
|
|
1757
|
+
`@sublang/playbook/runtime` — `adopt(session, snapshot, context)` — as a third
|
|
1758
|
+
initialization path distinct from `init` and same-engagement `restore`.
|
|
1759
|
+
Adoption may bind a retained generation to a fresh valid `PlaybookSession`
|
|
1760
|
+
identity. Every runtime the shared `createXStatePlaybookRuntime` factory
|
|
1761
|
+
constructs implements `adopt`, regardless of whether the artifact supplies
|
|
1762
|
+
retained-generation classification metadata; a bespoke runtime may omit it,
|
|
1763
|
+
and hosts feature-detect the capability by member presence.
|
|
1764
|
+
|
|
1765
|
+
Before actor construction or any player-session-store, port, trace, status,
|
|
1766
|
+
or telemetry effect, `adopt` shall validate and detach the target session, the
|
|
1767
|
+
snapshot, and an exact closed-schema `PlaybookAdoptionContext` whose nonempty
|
|
1768
|
+
`sourceSessionId` names the retained frame's source runtime session, whose
|
|
1769
|
+
nonempty `sourceGenerationId` names the retained stack root's source
|
|
1770
|
+
`rootSessionId`, and whose optional nonempty `targetChildSessionId` is present
|
|
1771
|
+
exactly when the snapshot carries a suspended call. The source session and
|
|
1772
|
+
generation ids shall coincide exactly for a root frame. The target session and
|
|
1773
|
+
root ids shall each differ from their source counterparts, and a supplied
|
|
1774
|
+
target child id shall differ from every source and target identity visible to
|
|
1775
|
+
that frame. Accessors, unknown or missing members, empty identities, and an
|
|
1776
|
+
inconsistent child mapping shall reject during preflight.
|
|
1777
|
+
|
|
1778
|
+
That preflight shall also validate the part of the exact structural envelope
|
|
1779
|
+
visible to the runtime: snapshot schema version `4`, target playbook id, the
|
|
1780
|
+
factory's already-validated artifact contract, and any supplied local-role
|
|
1781
|
+
binding set against the artifact's declared roles. The adopting host
|
|
1782
|
+
owns the working-directory and complete catalog-entry comparison — registry
|
|
1783
|
+
module identity, manifest command, options, and role set — plus every retained
|
|
1784
|
+
frame's artifact-schema comparison, and shall perform them before calling the
|
|
1785
|
+
runtime capability (DR-038 §3).
|
|
1786
|
+
The preflight shall apply the same exact full-mirror rule as restore: a linked
|
|
1787
|
+
workflow target receives a current-host mirror equal to the retained ledger,
|
|
1788
|
+
while the internal Captain target requires the retained ledger to be empty.
|
|
1789
|
+
|
|
1790
|
+
Adoption shall not restore any source counter. The fresh target trace, turn,
|
|
1791
|
+
judge-call, player-call, supported direct-Captain-call, playbook-call, and
|
|
1792
|
+
apply-call counter spaces shall start at zero. Before its session-start trace,
|
|
1793
|
+
a descriptor-free adoption leaves the playbook-call counter at zero. A
|
|
1794
|
+
suspended adoption instead consumes `playbook-1` as the fresh target call id,
|
|
1795
|
+
replaces the descriptor's source child id with `targetChildSessionId`, omits the
|
|
1796
|
+
source `turnId`, and sets the target playbook-call counter to one; it changes no
|
|
1797
|
+
opaque persisted machine value and makes no child-host call.
|
|
1798
|
+
|
|
1799
|
+
After preflight, adoption shall construct the persisted actor and prepare the
|
|
1800
|
+
nested bridge through the same transaction as restore, using the rebased
|
|
1801
|
+
descriptor or an explicit absence. Before actor startup it shall emit exactly
|
|
1802
|
+
one `session.started` as target trace sequence `1`, carrying the adopted
|
|
1803
|
+
top-level `state` and optional `stateId` plus an exact `adoption` object:
|
|
1804
|
+
|
|
1805
|
+
- without a suspended call, `{ sourceSessionId, sourceGenerationId }`;
|
|
1806
|
+
- with a suspended call, `{ sourceSessionId, sourceGenerationId,
|
|
1807
|
+
sourceCallId, sourceChildSessionId, targetCallId: 'playbook-1',
|
|
1808
|
+
targetChildSessionId }`, while the event also carries top-level
|
|
1809
|
+
`callId: 'playbook-1'` and no `turnId`.
|
|
1810
|
+
|
|
1811
|
+
The runtime shall then start the actor with inspection effects suppressed,
|
|
1812
|
+
claim the rebased descriptor, require the reconstructed active normalized
|
|
1813
|
+
state to equal the retained state under that rebase, drain suppressed work,
|
|
1814
|
+
and confirm the bridge as the final fallible step. A preflight mismatch emits
|
|
1815
|
+
nothing. A later state or bridge mismatch makes no child-host call or
|
|
1816
|
+
playbook-call start/finish boundary, rolls provisional ownership back, and,
|
|
1817
|
+
because the target start was attempted, performs failed-start cleanup with one
|
|
1818
|
+
best-effort target `session.disposed`; successful cleanup leaves the runtime
|
|
1819
|
+
reusable. A successful adoption shall close `init`, `restore`, and `adopt`
|
|
1820
|
+
under the ordinary one-start runtime lifecycle. Its immediate export shall
|
|
1821
|
+
carry trace sequence `1`, zero fresh turn, judge, and player counters, zero
|
|
1822
|
+
direct-Captain counter when supported, and playbook-call sequence zero or one
|
|
1823
|
+
according to the suspended shape. Later target turns and calls allocate from
|
|
1824
|
+
those fresh counters rather than continue any source id or sequence. Ordinary
|
|
1825
|
+
same-engagement restore remains trace-silent and preserves its source
|
|
1826
|
+
identities and counters exactly (DR-038 §5).
|
|
1827
|
+
|
|
1828
|
+
Adoption shall not apply the retained snapshot's `roleResumeTokens` through a
|
|
1829
|
+
supplied player-session store's `restore` operation or seed runtime-private
|
|
1830
|
+
continuation from them. For every later local-role invocation, any target
|
|
1831
|
+
session `roleBindings` are the sole source of supplied player and prompt
|
|
1832
|
+
identities, and any supplied player-session store is the sole conversation
|
|
1833
|
+
authority. The runtime shall resolve the current binding and, when a store is
|
|
1834
|
+
supplied, select it at the invocation boundary and pass the exact selected
|
|
1835
|
+
token or `false`. Where
|
|
1836
|
+
the ordinary continuation rules authorize a store mutation, that mutation
|
|
1837
|
+
shall target the same store. It shall never fall back to the retained token
|
|
1838
|
+
projection. A replacement binding whose current selection is `false` therefore
|
|
1839
|
+
starts fresh under its new identities; without a supplied store, the target
|
|
1840
|
+
runtime's private continuation starts empty (DR-038 §4).
|
|
1841
|
+
|
|
1842
|
+
## Retained-generation classification (optional)
|
|
1843
|
+
|
|
1844
|
+
A linked runtime may expose the optional read-only
|
|
1845
|
+
`retainedGenerationMetadata` marker of `@sublang/playbook/runtime` together
|
|
1846
|
+
with the parked-session snapshot pair and the independently feature-detected
|
|
1847
|
+
adoption capability so a Captain can retain its safe pre-terminal generations.
|
|
1848
|
+
Its `unfinishedFinalStateIds` array shall preserve the artifact's link-time
|
|
1849
|
+
declaration exactly, including an explicitly empty set, and shall be immutable
|
|
1850
|
+
and detached from that declaration. Absence means the runtime contributes no
|
|
1851
|
+
retained generation; presence supplies only terminal classification metadata
|
|
1852
|
+
and does not itself supply the adoption operation.
|
|
1853
|
+
Every runtime the shared `createXStatePlaybookRuntime` factory constructs from
|
|
1854
|
+
a supplied `unfinishedFinalStateIds` spec member shall expose the marker; a
|
|
1855
|
+
bespoke runtime opts into classification only by implementing the public member
|
|
1856
|
+
itself.
|
|
1857
|
+
|
|
1334
1858
|
## Control surface (optional)
|
|
1335
1859
|
|
|
1336
1860
|
A linked runtime may implement the optional control-surface capability of
|
|
@@ -1367,6 +1891,12 @@ type PlaybookControlReceipt =
|
|
|
1367
1891
|
// Optional PlaybookRuntime members — both or neither:
|
|
1368
1892
|
describe?(): PlaybookControlView;
|
|
1369
1893
|
apply?(input: { actionId: string; key: string; signal: AbortSignal }): Promise<PlaybookControlReceipt>;
|
|
1894
|
+
|
|
1895
|
+
// Independent optional host-only unresolved-envelope identity seam:
|
|
1896
|
+
unresolvedEffectEnvelopes?(): readonly (
|
|
1897
|
+
| { readonly kind: 'boundary'; readonly boundaryId: string }
|
|
1898
|
+
| { readonly kind: 'logical-operation'; readonly operationId: string }
|
|
1899
|
+
)[];
|
|
1370
1900
|
```
|
|
1371
1901
|
|
|
1372
1902
|
`describe()` shall be side-effect free — it emits no trace, status, or
|
|
@@ -1387,6 +1917,16 @@ identifier for it. A state whose source declares no description carries no
|
|
|
1387
1917
|
`stateDescription`: an id is never promoted into a description, so a host is
|
|
1388
1918
|
never handed an identifier dressed as meaning.
|
|
1389
1919
|
|
|
1920
|
+
At that same safe control-capture point, a schema-3 runtime that retains
|
|
1921
|
+
effect-possible outcome-unresolved work may expose
|
|
1922
|
+
`unresolvedEffectEnvelopes()` so its host can project the authoritative effect
|
|
1923
|
+
ledger. The method shall return only exact nonblank durable boundary or
|
|
1924
|
+
logical-operation identities in envelope order, shall return an empty list
|
|
1925
|
+
when no unresolved envelope remains, and shall expose no receipt, repository
|
|
1926
|
+
observation, semantic evidence, prose, or live authority. It is side-effect
|
|
1927
|
+
free on the same terms as `describe()`, and no returned identity or bounded
|
|
1928
|
+
repository evidence shall enter `PlaybookRunResult`.
|
|
1929
|
+
|
|
1390
1930
|
The view's `context` is an explicit projection the linked runtime **authors**,
|
|
1391
1931
|
never an allow-by-default serialization of the FSM context (PBRT-52).
|
|
1392
1932
|
Only the runtime knows which of its context members are safe and relevant
|
|
@@ -1417,15 +1957,23 @@ default. The rules:
|
|
|
1417
1957
|
Actions derive from the live snapshot, only at the same safe point the
|
|
1418
1958
|
parked-session snapshot uses (actor status `active`, quiescent, no pending
|
|
1419
1959
|
nested call); anywhere else `actions` is empty while the rest of the view
|
|
1420
|
-
still describes the state.
|
|
1421
|
-
|
|
1960
|
+
still describes the state.
|
|
1961
|
+
While effect-possible outcome evidence remains unresolved, the view shall omit its pending Boss questions and state description and shall replace every ordinary action with exactly `reconcile:unresolved-effect` labeled `Retry unresolved effect reconciliation` and `abandon:unresolved-effect` labeled `Abandon unresolved workflow attempt`.
|
|
1962
|
+
Otherwise two ordinary families exist, labeled from source state descriptions:
|
|
1422
1963
|
|
|
1423
1964
|
- **Failure-state retry** — while the singular state id is the recoverable
|
|
1424
|
-
failure state and the
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1965
|
+
failure state and the live snapshot accepts the retry event sourced below,
|
|
1966
|
+
the runtime shall advertise `retry:<EVENT_TYPE>` replaying exactly that
|
|
1967
|
+
event. Where the emitted module's entry-event declaration names the FSM
|
|
1968
|
+
context member the machine's entry action copies the exact Boss text into
|
|
1969
|
+
(DR-034), the retry event is that deterministic entry event built from the
|
|
1970
|
+
live snapshot's member — excluded when the member is absent, not a string,
|
|
1971
|
+
or blank, and never falling back to the record. Where it names no member,
|
|
1972
|
+
the retry event is the recorded last classified event (the event a public
|
|
1973
|
+
Boss boundary sent that drove the run into `failed`, kept with its recorded
|
|
1974
|
+
payload), and there is none while the runtime holds none. The member is
|
|
1975
|
+
declared, never inferred from a context member that happens to match the
|
|
1976
|
+
entry event's text field.
|
|
1429
1977
|
- **Jump entries** — for each registered resumable state id whose
|
|
1430
1978
|
explicit-state-jump event (`BOSS_INTERRUPT` with that `targetId`, optional
|
|
1431
1979
|
textual fields omitted) the live snapshot accepts, guards included, the
|
|
@@ -1456,8 +2004,8 @@ its key — a later call with that key revalidates afresh, traces its own
|
|
|
1456
2004
|
pair, and may execute once the action is advertised — and a key whose call
|
|
1457
2005
|
threw before reaching acceptance (lifecycle misuse, invalid input, a
|
|
1458
2006
|
pre-acceptance abort, a rejected start-boundary sink) likewise records
|
|
1459
|
-
nothing, so a later call with that key may execute.
|
|
1460
|
-
validated event through the same actor drive as `handleBossInput` — state
|
|
2007
|
+
nothing, so a later call with that key may execute.
|
|
2008
|
+
Executing an ordinary retry or jump sends the validated event through the same actor drive as `handleBossInput` — state
|
|
1461
2009
|
transitions, player/judge boundaries, statuses, and traces flow unchanged —
|
|
1462
2010
|
and settles `executed` with the projected run result, or `failed` with the
|
|
1463
2011
|
normalized error when the run settles in the failure state, aborts, or a
|
|
@@ -1468,6 +2016,11 @@ the paired `apply.started` / `apply.finished` events of §Playbook trace, and
|
|
|
1468
2016
|
`apply` shares the single active-boundary sentinel with `handleBossInput`
|
|
1469
2017
|
and `resumePlaybookCall`.
|
|
1470
2018
|
|
|
2019
|
+
Executing `reconcile:unresolved-effect` shall use only the current host's authoritative effect ledger for any reconciliation refresh and shall start no player.
|
|
2020
|
+
When an open deferred logical operation is checkpoint-restoration eligible, that action shall reacquire its exclusive repository claim and compare the current observation with the saved checkpoint; exact equality shall durably consume eligibility and return to the identical bound wait with its stable question through an ordinary nonterminal run result without a player, judge, or semantic-candidate delivery, while inequality or any other still-unresolved evidence shall return `no-action` and remain unresolved.
|
|
2021
|
+
Executing `abandon:unresolved-effect` shall move no FSM state or start any player, judge, Captain, script, or child call and shall settle `executed` with exactly `{ outcome: 'unresolved-effect', state }`, where `state` is the current normalized nonfinal state.
|
|
2022
|
+
That state-only run-result arm shall carry no `stateDescription`, output, pending call, error, repository receipt, effect ledger, semantic evidence, or other bounded effect fact, and shall claim neither an authored outcome nor workflow completion.
|
|
2023
|
+
|
|
1471
2024
|
Acceptance is also the line past which `apply` does not throw, and
|
|
1472
2025
|
publication — the `apply.finished` emission — is the line past which its
|
|
1473
2026
|
receipt no longer changes. A settlement failure after acceptance but before
|
|
@@ -1480,12 +2033,17 @@ trace and the return agree, and a receipt states what happened to the effect
|
|
|
1480
2033
|
rather than what happened to its telemetry. The published receipt stands, is
|
|
1481
2034
|
returned and replayed verbatim, and the delivery failure travels on the
|
|
1482
2035
|
runtime's emission-failure channel to surface from the next public boundary
|
|
1483
|
-
that drains
|
|
2036
|
+
that drains — unless the delivery failure is causally identical to the apply
|
|
2037
|
+
signal's own abort reason, in which case it evidences the cancellation and is
|
|
2038
|
+
dropped, not latched
|
|
2039
|
+
([DR-036](../specs/decisions/036-coherent-abort-settlement.md)).
|
|
1484
2040
|
|
|
1485
2041
|
The recorded receipts and the recorded last classified event are
|
|
1486
|
-
process-local: the durable runtime snapshot persists neither
|
|
1487
|
-
|
|
1488
|
-
|
|
2042
|
+
process-local: the durable runtime snapshot persists neither. A restored
|
|
2043
|
+
runtime therefore advertises the retry of a declared entry-event source
|
|
2044
|
+
immediately — that payload rides the persisted machine snapshot — while a
|
|
2045
|
+
module declaring no source advertises a retry again only after its next
|
|
2046
|
+
classified event.
|
|
1489
2047
|
|
|
1490
2048
|
## Abort
|
|
1491
2049
|
|
|
@@ -1499,7 +2057,61 @@ the shared `combineAbortSignals`). Classify a rejection as cancellation by its
|
|
|
1499
2057
|
causal identity with the applicable signal reason, not by an `AbortError` name
|
|
1500
2058
|
or by observing only that the signal is also aborted. Signals may carry an
|
|
1501
2059
|
ordinary `Error`, while a distinct transport or sink failure that occurs after
|
|
1502
|
-
abort remains a non-abort control error and takes precedence.
|
|
2060
|
+
abort remains a non-abort control error and takes precedence. Classification
|
|
2061
|
+
lives at each latch or report site, against the boundary signal applicable
|
|
2062
|
+
there — the invocation-lifetime combined signal, and during a resume that
|
|
2063
|
+
boundary's own signal — so a failure causally identical to the applicable
|
|
2064
|
+
reason is the cancellation's own evidence: it is handled there under the phase
|
|
2065
|
+
rules below, never mislabeled as a distinct failure and never carried to an
|
|
2066
|
+
unrelated later boundary
|
|
2067
|
+
([DR-036](../specs/decisions/036-coherent-abort-settlement.md)).
|
|
2068
|
+
A failure already latched as distinct retains that ownership; a later drain
|
|
2069
|
+
shall not reinterpret it against another boundary whose abort signal happens
|
|
2070
|
+
to use the same object as its reason.
|
|
2071
|
+
A public boundary settles on the machine's state at its quiescence point,
|
|
2072
|
+
in this precedence: a suspended pending call, then a distinct actor error,
|
|
2073
|
+
then terminal completion, then a coincident abort, then the recoverable
|
|
2074
|
+
failure state — a completed machine settles `terminal` even when the signal
|
|
2075
|
+
also aborted, because an `aborted` settlement over a terminal machine hides
|
|
2076
|
+
work the next turn would silently restart.
|
|
2077
|
+
An abort observed after the outcome is computed does not rewrite it, and a
|
|
2078
|
+
settlement-channel rejection causally identical to the abort reason is
|
|
2079
|
+
forgiven, so the returned result and the settlement trace state one fact.
|
|
2080
|
+
A boundary entered with an already-aborted signal delivers nothing.
|
|
2081
|
+
That entry refusal precedes the ordinary settlement order: a pre-aborted
|
|
2082
|
+
resume reports `aborted` while preserving its suspended pending call rather
|
|
2083
|
+
than reporting `suspended` for work it did not deliver.
|
|
2084
|
+
Cancellation-coupled channel rejections obey this phase matrix:
|
|
2085
|
+
|
|
2086
|
+
- **Before a host call or effect starts (and before apply acceptance):** an
|
|
2087
|
+
identical start-channel rejection starts no host call or effect and latches
|
|
2088
|
+
no control error. A recorded start receives one best-effort `aborted` finish.
|
|
2089
|
+
An ordinary run boundary then settles by the precedence above; a
|
|
2090
|
+
pre-acceptance `apply` instead rejects with that exact reason, records no
|
|
2091
|
+
receipt, and leaves its key reusable.
|
|
2092
|
+
- **After a host call or effect starts but before its finish or outcome is
|
|
2093
|
+
recorded:** an identical host, cleanup, observer, or in-flight-emission
|
|
2094
|
+
rejection is cancellation evidence. Invocation-owned cleanup completes, a
|
|
2095
|
+
started trace pair receives one `aborted` finish, and the ordinary boundary
|
|
2096
|
+
settles by the precedence above. A distinct rejection remains a control
|
|
2097
|
+
failure, produces the applicable error finish, and takes distinct-error
|
|
2098
|
+
precedence.
|
|
2099
|
+
- **After a call finish is recorded but before the enclosing non-apply outcome
|
|
2100
|
+
is computed:** an identical finish-sink or drain rejection leaves the
|
|
2101
|
+
recorded finish unchanged, emits no corrective second finish, latches
|
|
2102
|
+
nothing, and lets the enclosing boundary settle by the precedence above.
|
|
2103
|
+
- **After apply acceptance but before receipt publication:** every settlement
|
|
2104
|
+
failure, the exact apply abort reason included, is folded into the current
|
|
2105
|
+
`failed` receipt. Acceptance forbids throwing; the replacement receipt is
|
|
2106
|
+
published, returned, and replayed, and the failure is not carried as a later
|
|
2107
|
+
delivery error.
|
|
2108
|
+
- **After a non-apply outcome is computed or an apply receipt is published:**
|
|
2109
|
+
an identical rejection is dropped without rewriting the outcome or receipt
|
|
2110
|
+
and without poisoning a later boundary. A distinct non-apply settlement
|
|
2111
|
+
rejection retains current-boundary control-error precedence; a distinct
|
|
2112
|
+
post-publication apply rejection retains the published receipt and travels
|
|
2113
|
+
on the delivery-failure channel to the next boundary that drains.
|
|
2114
|
+
On abort, the
|
|
1503
2115
|
runtime shall not merely race the imperative
|
|
1504
2116
|
wait and return while an invocation remains live: it shall let the selected
|
|
1505
2117
|
rejection path settle and drive the actor to a quiescent state before returning
|
|
@@ -1563,16 +2175,23 @@ The `playbook.trace` copies are the host-agnostic runtime-boundary record requir
|
|
|
1563
2175
|
## Output
|
|
1564
2176
|
|
|
1565
2177
|
The link compiler emits one TypeScript module per playbook.
|
|
1566
|
-
|
|
1567
|
-
|
|
2178
|
+
Every linked artifact shall emit an `unfinishedFinalStateIds` set beside its resumable-state registry as mechanical link-time metadata.
|
|
2179
|
+
The set shall contain exactly the stable ids of root `type: 'final'` states whose terminal outcomes leave the procedure unfinished, and shall be explicitly empty when no terminal outcome does.
|
|
2180
|
+
The linker shall not infer the set from a state description, opaque output, or procedure prose.
|
|
2181
|
+
The linker shall reject a declared id that does not name a root final state, and the shared factory shall independently reject it at construction before runtime effects.
|
|
2182
|
+
For a factory-backed artifact the set is a `spec` member; a bespoke artifact shall retain equivalent linked metadata, and the artifact declaration is not itself the public runtime retention marker or an adoption capability.
|
|
2183
|
+
For an FSM that declares no `type: 'parallel'` state — necessarily flat
|
|
2184
|
+
under [gears2fsm.md](gears2fsm.md)'s one-state-per-item mapping — it shall
|
|
2185
|
+
emit the thin shared-factory module defined below.
|
|
1568
2186
|
For an FSM that declares a parallel state, it shall emit bespoke linked
|
|
1569
2187
|
machinery satisfying this document's runtime contract and shall not invoke
|
|
1570
|
-
`createXStatePlaybookRuntime`, whose supported domain is single-region
|
|
1571
|
-
under [DR-019](../specs/decisions/019-shared-linked-runtime-factory.md).
|
|
2188
|
+
`createXStatePlaybookRuntime`, whose supported domain is flat single-region
|
|
2189
|
+
FSMs under [DR-019](../specs/decisions/019-shared-linked-runtime-factory.md).
|
|
1572
2190
|
The FSM-interpreter machinery — actor wiring, boundary tracing, Boss-event
|
|
1573
2191
|
mapping, adjudication, script execution, nested-playbook bridging, session
|
|
1574
|
-
lifecycle, abort handling, and the optional parked-session snapshot
|
|
1575
|
-
|
|
2192
|
+
lifecycle, abort handling, and the optional parked-session snapshot and
|
|
2193
|
+
retained-snapshot adoption capabilities — is not regenerated for a
|
|
2194
|
+
factory-backed artifact: it ships once
|
|
1576
2195
|
as the shared `createXStatePlaybookRuntime(machine, spec)` factory exported by
|
|
1577
2196
|
`@sublang/playbook/xstate-runtime`, and the emitted module hands its FSM and
|
|
1578
2197
|
a small per-playbook `spec` to that factory. Every behavioral section of
|
|
@@ -1613,7 +2232,10 @@ The thin emitted module:
|
|
|
1613
2232
|
actor.
|
|
1614
2233
|
- Supplies in `spec` only what the factory cannot read from the FSM
|
|
1615
2234
|
artifact's own data: the deterministic textual entry event where
|
|
1616
|
-
§Boss-event mapping prescribes deterministic entry
|
|
2235
|
+
§Boss-event mapping prescribes deterministic entry, naming with it the FSM
|
|
2236
|
+
context member that event's own transition action copies the exact Boss
|
|
2237
|
+
text into wherever the machine keeps one, so the failure-state retry of
|
|
2238
|
+
§Control surface survives a restore; compact `bossEvents`
|
|
1617
2239
|
metadata for each additional Boss-union arm whose exact required/optional
|
|
1618
2240
|
judge fields, runtime-owned text fields, or closed string values disappear
|
|
1619
2241
|
under TypeScript erasure; `placeholderFields` only for authored token/field
|
|
@@ -1623,9 +2245,13 @@ The thin emitted module:
|
|
|
1623
2245
|
complete `roleStates` status map derived from every FSM state that invokes
|
|
1624
2246
|
the typed `player` actor, with each `role` copied from that state's
|
|
1625
2247
|
source-derived `meta.playbook.role` (an empty map when there is no such
|
|
1626
|
-
state); the
|
|
2248
|
+
state); the exact schema-3 `outcomeAuthority` map derived
|
|
2249
|
+
from every such state's `invoke.input.result` contract and its linked field
|
|
2250
|
+
authorities and repository dispositions (an explicit empty governed map
|
|
2251
|
+
when there is no such state); the
|
|
1627
2252
|
`verbatimPayloadFields` set derived from annotated result fields above; the
|
|
1628
|
-
`
|
|
2253
|
+
explicitly empty or populated `unfinishedFinalStateIds` set declared above;
|
|
2254
|
+
the `controlContextFields` projection of §Control surface; and any
|
|
1629
2255
|
per-playbook strategy override (classifier, prompt composers,
|
|
1630
2256
|
required-field extraction, status formatting) an earlier section of this
|
|
1631
2257
|
definition requires for that playbook.
|
|
@@ -1682,9 +2308,8 @@ The thin emitted module:
|
|
|
1682
2308
|
runtime-owned arm to have lost payload detail under erasure shall report
|
|
1683
2309
|
that gap rather than emit the entry.
|
|
1684
2310
|
- Supplies `spec.compat` with the compatibility values current at link time:
|
|
1685
|
-
`{ artifactSchema, runtimeAbi }`, where `
|
|
1686
|
-
|
|
1687
|
-
`runtimeAbi` is the installed shared engine's `RUNTIME_ABI` self-report.
|
|
2311
|
+
`{ artifactSchema: 3, runtimeAbi }`, where `runtimeAbi` is the installed shared engine's
|
|
2312
|
+
`RUNTIME_ABI` self-report.
|
|
1688
2313
|
The linker shall verify that the installed engine lists the emitted
|
|
1689
2314
|
schema in `SUPPORTED_ARTIFACT_SCHEMAS` and treat its absence as a
|
|
1690
2315
|
link-time error; it shall not stamp a different member (such as the
|
|
@@ -1695,9 +2320,29 @@ The thin emitted module:
|
|
|
1695
2320
|
module and fails construction on a mismatch, so an artifact linked under
|
|
1696
2321
|
one engine cannot run silently skewed under another. Modules emitted
|
|
1697
2322
|
before this contract carry no `compat` member and shall reject before interpretation.
|
|
1698
|
-
- Requires the containing public registry manifest to advertise the
|
|
1699
|
-
|
|
1700
|
-
`
|
|
2323
|
+
- Requires the containing public registry manifest to advertise the identical
|
|
2324
|
+
`artifactSchema` and an exact implementation `runtimeProfile`. A shared
|
|
2325
|
+
factory profile is `{ kind: 'shared-factory', compat }`, where `compat` is
|
|
2326
|
+
the immutable compatibility record captured by that actual factory from
|
|
2327
|
+
its validated `spec.compat`; a bespoke profile is
|
|
2328
|
+
`{ kind: 'bespoke', artifactSchema }`, with schema `3` declared directly by
|
|
2329
|
+
that implementation and no `runtimeAbi` claim. A registry factory accepts configured options and current
|
|
2330
|
+
host capabilities separately and composes the linked runtime's exact
|
|
2331
|
+
`{ configuredOptions, hostCapabilities }` input. The Captain host shall
|
|
2332
|
+
capture the imported manifest fields once, require capabilities for every
|
|
2333
|
+
and only enabled artifact id, validate each capability's artifact, role,
|
|
2334
|
+
cohort, and canonical-worktree authority, and reject a missing, extra,
|
|
2335
|
+
malformed, or mismatched capability before runtime construction.
|
|
2336
|
+
- Default-exports the factory call as `createPlaybookRuntime`, typed as
|
|
2337
|
+
`XStatePlaybookRuntimeFactory<XStatePlaybookRuntimeConstruction<PlaybookRuntimeOptions, HostCapabilities>, 3>`
|
|
2338
|
+
with the artifact's declared live capability type. A registry module loads
|
|
2339
|
+
dynamically inside the host's caught boundary, so its eager module-scope
|
|
2340
|
+
factory call fails fast there. The compiled session Captain module is the
|
|
2341
|
+
exception: the shell and both CLI front ends import it statically, so it
|
|
2342
|
+
shall defer its factory call to the first runtime request — an eager call
|
|
2343
|
+
would turn a future `spec.compat` rejection into an uncaught module-load
|
|
2344
|
+
error that takes even `--help` down, instead of the caught
|
|
2345
|
+
host-construction boundary's setup diagnostic.
|
|
1701
2346
|
- Exposes, under an `_internal` export, the pure helpers verification
|
|
1702
2347
|
needs — at least the prompt composers its own machine uses, which may
|
|
1703
2348
|
re-export the shared defaults when the spec does not override composition —
|
|
@@ -1801,7 +2446,7 @@ New behavior in any of these areas requires a separate slc spec.
|
|
|
1801
2446
|
|
|
1802
2447
|
## References
|
|
1803
2448
|
|
|
1804
|
-
[1]:
|
|
1805
|
-
[2]:
|
|
2449
|
+
[1]: text2gears.md "First phase: text → GEARS spec items."
|
|
2450
|
+
[2]: gears2fsm.md "Second phase: GEARS items → FSM artifact."
|
|
1806
2451
|
[3]: https://stately.ai/docs/actors "XState actors — `createActor`, snapshots, abort signal handling."
|
|
1807
2452
|
[4]: https://github.com/sindresorhus/p-queue#readme "p-queue concurrency and AbortSignal support."
|