@sublang/playbook 14.0.0 → 15.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 +1 -1
- package/docs/cli.md +14 -14
- package/docs/configuration.md +15 -9
- package/package.json +1 -1
- package/reference/sdlc/branch.playbook/branch.fsm.d.ts +1 -1
- package/reference/sdlc/code.md +1 -1
- package/reference/sdlc/code.playbook/bin/launch-config.js +55 -16
- package/reference/sdlc/code.playbook/bin/playbook.js +14 -9
- package/reference/sdlc/code.playbook/bin/repository-effects.js +187 -14
- package/reference/sdlc/code.playbook/bin/run.js +12 -8
- package/reference/sdlc/code.playbook/code.fsm.d.ts +1 -1
- package/reference/sdlc/code.playbook/code.fsm.js +1 -1
- package/reference/sdlc/code.playbook/code.fsm.ts +1 -1
- package/reference/sdlc/code.playbook/code.gears.md +2 -2
- package/reference/sdlc/code.playbook/host-capabilities.d.ts +6 -0
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +3 -1
- package/reference/sdlc/code.playbook/playbook-captain.js +357 -16
- package/reference/sdlc/code.playbook/playbook-captain.ts +459 -34
- package/reference/sdlc/code.playbook/session-host.d.ts +6 -3
- package/reference/sdlc/decide.md +1 -1
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +1 -1
- package/reference/sdlc/decide.playbook/decide.fsm.js +1 -1
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1 -1
- package/reference/sdlc/decide.playbook/decide.gears.md +1 -1
- package/reference/sdlc/decide.playbook/decide.playbook.js +25 -1
- package/reference/sdlc/decide.playbook/decide.playbook.ts +35 -2
- package/reference/sdlc/dev.md +15 -8
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +1 -1
- package/reference/sdlc/dev.playbook/dev.fsm.js +18 -12
- package/reference/sdlc/dev.playbook/dev.fsm.ts +18 -13
- package/reference/sdlc/dev.playbook/dev.gears.md +19 -12
- package/reference/sdlc/dev.playbook/dev.playbook.js +1 -1
- package/reference/sdlc/dev.playbook/dev.playbook.ts +1 -2
- package/reference/sdlc/dev.playbook/dev.registry.js +1 -1
- package/reference/sdlc/dev.playbook/dev.registry.ts +1 -1
- package/reference/sdlc/pr.playbook/pr.fsm.d.ts +1 -1
- package/reference/sdlc/review.md +1 -1
- package/reference/sdlc/review.playbook/review.fsm.d.ts +1 -1
- package/reference/sdlc/review.playbook/review.fsm.js +1 -1
- package/reference/sdlc/review.playbook/review.fsm.ts +1 -1
- package/reference/sdlc/review.playbook/review.gears.md +1 -1
- package/slc/link.md +32 -0
- package/src/runtime.d.ts +70 -0
- package/src/runtime.js +232 -1
- package/src/runtime.ts +358 -1
- package/src/xstate-playbook-runtime.d.ts +9 -10
- package/src/xstate-playbook-runtime.js +372 -20
- package/src/xstate-playbook-runtime.ts +460 -19
- package/src/xstate-runtime.d.ts +9 -1
- package/src/xstate-runtime.js +299 -19
- package/src/xstate-runtime.ts +362 -21
package/src/runtime.ts
CHANGED
|
@@ -57,10 +57,342 @@ export type JsonValue =
|
|
|
57
57
|
| readonly JsonValue[]
|
|
58
58
|
| { readonly [key: string]: JsonValue };
|
|
59
59
|
|
|
60
|
+
// DR-063 §1: the closed list of failure codes. There is no unknown code — an
|
|
61
|
+
// unrecognized failure is `runtime-defect` carrying its message as `reason` —
|
|
62
|
+
// so a host catalogue is complete exactly when it covers this list.
|
|
63
|
+
export const PLAYBOOK_FAILURE_CODES = [
|
|
64
|
+
'commit-missing',
|
|
65
|
+
'commit-residual',
|
|
66
|
+
'pre-existing-lost',
|
|
67
|
+
'commits-more-than-one',
|
|
68
|
+
'history-rewritten',
|
|
69
|
+
'foreign-change',
|
|
70
|
+
'observation-unstable',
|
|
71
|
+
'attribution-ambiguous',
|
|
72
|
+
'receipt-missing',
|
|
73
|
+
'judge-failed',
|
|
74
|
+
'player-failed',
|
|
75
|
+
'aborted',
|
|
76
|
+
'child-failed',
|
|
77
|
+
'runtime-defect',
|
|
78
|
+
] as const;
|
|
79
|
+
|
|
80
|
+
export type PlaybookFailureCode = (typeof PLAYBOOK_FAILURE_CODES)[number];
|
|
81
|
+
|
|
82
|
+
/** The bounded path lists one failure code's evidence may carry (DR-063 §1). */
|
|
83
|
+
export interface PlaybookFailurePaths {
|
|
84
|
+
/** Paths changed in the worktree that no commit carries. */
|
|
85
|
+
readonly uncommitted?: readonly string[];
|
|
86
|
+
/** Pre-existing baseline paths the call changed before committing. */
|
|
87
|
+
readonly altered?: readonly string[];
|
|
88
|
+
/** Pre-existing baseline paths whose content is nowhere. */
|
|
89
|
+
readonly lost?: readonly string[];
|
|
90
|
+
/** Paths whose entry differs or is missing on either side. */
|
|
91
|
+
readonly changed?: readonly string[];
|
|
92
|
+
/** Paths omitted from the lists above by the 32-path bound. */
|
|
93
|
+
readonly truncated?: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The bounded error record a failure cause may carry (DR-063 §1). */
|
|
97
|
+
export interface PlaybookFailureErrorEvidence {
|
|
98
|
+
readonly name: string;
|
|
99
|
+
readonly message: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The closed evidence object of one failure cause (DR-063 §1). */
|
|
103
|
+
export interface PlaybookFailureEvidence {
|
|
104
|
+
readonly required?: PlaybookRepositoryDisposition;
|
|
105
|
+
readonly observed?: PlaybookRepositoryReceipt['classification'];
|
|
106
|
+
readonly baselineHead?: string;
|
|
107
|
+
readonly afterHead?: string;
|
|
108
|
+
readonly commitOid?: string;
|
|
109
|
+
readonly paths?: PlaybookFailurePaths;
|
|
110
|
+
readonly reason?: string;
|
|
111
|
+
readonly error?: PlaybookFailureErrorEvidence;
|
|
112
|
+
readonly errorCode?: string;
|
|
113
|
+
readonly roleId?: string;
|
|
114
|
+
readonly playerId?: string;
|
|
115
|
+
readonly playbookId?: string;
|
|
116
|
+
readonly cause?: PlaybookFailureCause;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Why a parked workflow failed (DR-063 §1): one code from the closed list and
|
|
121
|
+
* the closed evidence object that code names. Evidence holds repository paths,
|
|
122
|
+
* dispositions, classifications, and revision identities only — never file
|
|
123
|
+
* content, player prose, or internal call and session identities.
|
|
124
|
+
*/
|
|
125
|
+
export interface PlaybookFailureCause {
|
|
126
|
+
readonly code: PlaybookFailureCode;
|
|
127
|
+
readonly evidence: PlaybookFailureEvidence;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Evidence members admitted for each code, in the order a sentence reads them. */
|
|
131
|
+
const FAILURE_EVIDENCE_MEMBERS: Readonly<
|
|
132
|
+
Record<
|
|
133
|
+
PlaybookFailureCode,
|
|
134
|
+
{
|
|
135
|
+
readonly required: readonly string[];
|
|
136
|
+
readonly optional: readonly string[];
|
|
137
|
+
readonly paths: readonly (keyof PlaybookFailurePaths)[];
|
|
138
|
+
}
|
|
139
|
+
>
|
|
140
|
+
> = {
|
|
141
|
+
'commit-missing': {
|
|
142
|
+
required: ['required', 'observed', 'baselineHead', 'afterHead', 'paths'],
|
|
143
|
+
optional: [],
|
|
144
|
+
paths: ['uncommitted'],
|
|
145
|
+
},
|
|
146
|
+
'commit-residual': {
|
|
147
|
+
required: ['required', 'observed', 'baselineHead', 'afterHead', 'paths'],
|
|
148
|
+
optional: ['commitOid'],
|
|
149
|
+
paths: ['uncommitted', 'altered'],
|
|
150
|
+
},
|
|
151
|
+
'pre-existing-lost': {
|
|
152
|
+
required: ['required', 'observed', 'baselineHead', 'paths'],
|
|
153
|
+
optional: ['afterHead'],
|
|
154
|
+
paths: ['lost'],
|
|
155
|
+
},
|
|
156
|
+
'commits-more-than-one': {
|
|
157
|
+
required: ['required', 'observed', 'baselineHead', 'afterHead'],
|
|
158
|
+
optional: [],
|
|
159
|
+
paths: [],
|
|
160
|
+
},
|
|
161
|
+
'history-rewritten': {
|
|
162
|
+
required: ['required', 'observed', 'baselineHead', 'afterHead'],
|
|
163
|
+
optional: [],
|
|
164
|
+
paths: [],
|
|
165
|
+
},
|
|
166
|
+
'foreign-change': {
|
|
167
|
+
required: ['required', 'observed', 'baselineHead', 'afterHead', 'paths'],
|
|
168
|
+
optional: [],
|
|
169
|
+
paths: ['changed'],
|
|
170
|
+
},
|
|
171
|
+
'observation-unstable': {
|
|
172
|
+
required: ['required', 'observed', 'baselineHead'],
|
|
173
|
+
optional: [],
|
|
174
|
+
paths: [],
|
|
175
|
+
},
|
|
176
|
+
'attribution-ambiguous': {
|
|
177
|
+
required: ['required', 'observed', 'baselineHead'],
|
|
178
|
+
optional: ['afterHead'],
|
|
179
|
+
paths: [],
|
|
180
|
+
},
|
|
181
|
+
'receipt-missing': { required: ['baselineHead'], optional: [], paths: [] },
|
|
182
|
+
'judge-failed': { required: ['reason'], optional: ['error'], paths: [] },
|
|
183
|
+
'player-failed': {
|
|
184
|
+
required: ['roleId', 'error'],
|
|
185
|
+
optional: ['playerId', 'errorCode'],
|
|
186
|
+
paths: [],
|
|
187
|
+
},
|
|
188
|
+
aborted: { required: [], optional: [], paths: [] },
|
|
189
|
+
'child-failed': { required: ['playbookId'], optional: ['cause'], paths: [] },
|
|
190
|
+
'runtime-defect': { required: ['reason'], optional: [], paths: [] },
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const FAILURE_PATH_LIMIT = 32;
|
|
194
|
+
const FAILURE_CAUSE_DEPTH_LIMIT = 4;
|
|
195
|
+
|
|
196
|
+
const REPOSITORY_DISPOSITIONS: readonly PlaybookRepositoryDisposition[] = [
|
|
197
|
+
'unchanged',
|
|
198
|
+
'one-descendant-commit',
|
|
199
|
+
'deferred',
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const RECEIPT_CLASSIFICATIONS: readonly PlaybookRepositoryReceipt['classification'][] =
|
|
203
|
+
[
|
|
204
|
+
'unchanged',
|
|
205
|
+
'one-descendant-commit',
|
|
206
|
+
'multiple-commits',
|
|
207
|
+
'rewritten-or-non-descendant',
|
|
208
|
+
'worktree-only-change',
|
|
209
|
+
'concurrent-or-foreign-change',
|
|
210
|
+
'observation-ambiguous',
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
function failureCauseRecord(
|
|
214
|
+
value: unknown,
|
|
215
|
+
path: string,
|
|
216
|
+
): Record<string, unknown> {
|
|
217
|
+
if (
|
|
218
|
+
value === null ||
|
|
219
|
+
typeof value !== 'object' ||
|
|
220
|
+
Array.isArray(value) ||
|
|
221
|
+
(Object.getPrototypeOf(value) !== Object.prototype &&
|
|
222
|
+
Object.getPrototypeOf(value) !== null)
|
|
223
|
+
) {
|
|
224
|
+
throw new TypeError(`${path} must be a plain object`);
|
|
225
|
+
}
|
|
226
|
+
return value as Record<string, unknown>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function failureCauseString(value: unknown, path: string): string {
|
|
230
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
231
|
+
throw new TypeError(`${path} must be a nonempty string`);
|
|
232
|
+
}
|
|
233
|
+
return value;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function failureCausePathList(value: unknown, path: string): readonly string[] {
|
|
237
|
+
if (!Array.isArray(value)) throw new TypeError(`${path} must be an array`);
|
|
238
|
+
if (value.length > FAILURE_PATH_LIMIT) {
|
|
239
|
+
throw new TypeError(`${path} must hold at most ${FAILURE_PATH_LIMIT} paths`);
|
|
240
|
+
}
|
|
241
|
+
const paths = value.map((entry, index) =>
|
|
242
|
+
failureCauseString(entry, `${path}[${index}]`),
|
|
243
|
+
);
|
|
244
|
+
for (const [index, entry] of paths.entries()) {
|
|
245
|
+
if (index > 0 && !(paths[index - 1]! < entry)) {
|
|
246
|
+
throw new TypeError(`${path} must be sorted and free of duplicates`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return Object.freeze(paths);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function failureCausePaths(
|
|
253
|
+
value: unknown,
|
|
254
|
+
admitted: readonly (keyof PlaybookFailurePaths)[],
|
|
255
|
+
path: string,
|
|
256
|
+
): PlaybookFailurePaths {
|
|
257
|
+
const record = failureCauseRecord(value, path);
|
|
258
|
+
const allowed = new Set<string>([...admitted, 'truncated']);
|
|
259
|
+
for (const key of Object.keys(record)) {
|
|
260
|
+
if (!allowed.has(key)) {
|
|
261
|
+
throw new TypeError(`${path} must not carry ${JSON.stringify(key)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const paths: Record<string, unknown> = {};
|
|
265
|
+
for (const member of admitted) {
|
|
266
|
+
if (!Object.hasOwn(record, member)) {
|
|
267
|
+
throw new TypeError(`${path}.${member} is required`);
|
|
268
|
+
}
|
|
269
|
+
paths[member] = failureCausePathList(record[member], `${path}.${member}`);
|
|
270
|
+
}
|
|
271
|
+
if (Object.hasOwn(record, 'truncated')) {
|
|
272
|
+
const truncated = record.truncated;
|
|
273
|
+
if (
|
|
274
|
+
typeof truncated !== 'number' ||
|
|
275
|
+
!Number.isSafeInteger(truncated) ||
|
|
276
|
+
truncated <= 0
|
|
277
|
+
) {
|
|
278
|
+
throw new TypeError(`${path}.truncated must be a positive integer`);
|
|
279
|
+
}
|
|
280
|
+
paths.truncated = truncated;
|
|
281
|
+
}
|
|
282
|
+
return Object.freeze(paths) as PlaybookFailurePaths;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function failureCauseErrorEvidence(
|
|
286
|
+
value: unknown,
|
|
287
|
+
path: string,
|
|
288
|
+
): PlaybookFailureErrorEvidence {
|
|
289
|
+
const record = failureCauseRecord(value, path);
|
|
290
|
+
for (const key of Object.keys(record)) {
|
|
291
|
+
if (key !== 'name' && key !== 'message') {
|
|
292
|
+
throw new TypeError(`${path} must not carry ${JSON.stringify(key)}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (typeof record.message !== 'string') {
|
|
296
|
+
throw new TypeError(`${path}.message must be a string`);
|
|
297
|
+
}
|
|
298
|
+
return Object.freeze({
|
|
299
|
+
name: failureCauseString(record.name, `${path}.name`),
|
|
300
|
+
message: record.message,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function validateFailureCause(
|
|
305
|
+
value: unknown,
|
|
306
|
+
path: string,
|
|
307
|
+
depth: number,
|
|
308
|
+
): PlaybookFailureCause {
|
|
309
|
+
if (depth > FAILURE_CAUSE_DEPTH_LIMIT) {
|
|
310
|
+
throw new TypeError(
|
|
311
|
+
`${path} must nest at most ${FAILURE_CAUSE_DEPTH_LIMIT} causes`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const record = failureCauseRecord(value, path);
|
|
315
|
+
for (const key of Object.keys(record)) {
|
|
316
|
+
if (key !== 'code' && key !== 'evidence') {
|
|
317
|
+
throw new TypeError(`${path} must not carry ${JSON.stringify(key)}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const code = record.code;
|
|
321
|
+
if (
|
|
322
|
+
typeof code !== 'string' ||
|
|
323
|
+
!(PLAYBOOK_FAILURE_CODES as readonly string[]).includes(code)
|
|
324
|
+
) {
|
|
325
|
+
throw new TypeError(`${path}.code must be one of the declared codes`);
|
|
326
|
+
}
|
|
327
|
+
const spec = FAILURE_EVIDENCE_MEMBERS[code as PlaybookFailureCode];
|
|
328
|
+
const evidenceRecord = failureCauseRecord(record.evidence, `${path}.evidence`);
|
|
329
|
+
const allowed = new Set<string>([...spec.required, ...spec.optional]);
|
|
330
|
+
for (const key of Object.keys(evidenceRecord)) {
|
|
331
|
+
if (!allowed.has(key)) {
|
|
332
|
+
throw new TypeError(
|
|
333
|
+
`${path}.evidence must not carry ${JSON.stringify(key)}`,
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const evidence: Record<string, unknown> = {};
|
|
338
|
+
for (const member of [...spec.required, ...spec.optional]) {
|
|
339
|
+
const required = spec.required.includes(member);
|
|
340
|
+
if (!Object.hasOwn(evidenceRecord, member)) {
|
|
341
|
+
if (required) {
|
|
342
|
+
throw new TypeError(`${path}.evidence.${member} is required`);
|
|
343
|
+
}
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const memberValue = evidenceRecord[member];
|
|
347
|
+
const memberPath = `${path}.evidence.${member}`;
|
|
348
|
+
if (member === 'required') {
|
|
349
|
+
if (!REPOSITORY_DISPOSITIONS.includes(memberValue as never)) {
|
|
350
|
+
throw new TypeError(`${memberPath} must be a repository disposition`);
|
|
351
|
+
}
|
|
352
|
+
evidence[member] = memberValue;
|
|
353
|
+
} else if (member === 'observed') {
|
|
354
|
+
if (!RECEIPT_CLASSIFICATIONS.includes(memberValue as never)) {
|
|
355
|
+
throw new TypeError(`${memberPath} must be a receipt classification`);
|
|
356
|
+
}
|
|
357
|
+
evidence[member] = memberValue;
|
|
358
|
+
} else if (member === 'paths') {
|
|
359
|
+
evidence[member] = failureCausePaths(memberValue, spec.paths, memberPath);
|
|
360
|
+
} else if (member === 'error') {
|
|
361
|
+
evidence[member] = failureCauseErrorEvidence(memberValue, memberPath);
|
|
362
|
+
} else if (member === 'cause') {
|
|
363
|
+
evidence[member] = validateFailureCause(
|
|
364
|
+
memberValue,
|
|
365
|
+
memberPath,
|
|
366
|
+
depth + 1,
|
|
367
|
+
);
|
|
368
|
+
} else {
|
|
369
|
+
evidence[member] = failureCauseString(memberValue, memberPath);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return Object.freeze({
|
|
373
|
+
code: code as PlaybookFailureCode,
|
|
374
|
+
evidence: Object.freeze(evidence) as PlaybookFailureEvidence,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Validate, detach, and freeze one failure cause (DR-063 §1). The check is
|
|
380
|
+
* closed per code: exactly the evidence members that code names, nothing else,
|
|
381
|
+
* and a `child-failed` cause nests at most four deep. A value this rejects is
|
|
382
|
+
* not a cause, so a caller omits it rather than publishing an invented one.
|
|
383
|
+
*/
|
|
384
|
+
export function assertPlaybookFailureCause(
|
|
385
|
+
value: unknown,
|
|
386
|
+
): PlaybookFailureCause {
|
|
387
|
+
return validateFailureCause(value, 'playbook failure cause', 1);
|
|
388
|
+
}
|
|
389
|
+
|
|
60
390
|
export interface NormalizedError {
|
|
61
391
|
name: string;
|
|
62
392
|
message: string;
|
|
63
393
|
stack?: string;
|
|
394
|
+
/** DR-063 §2: present exactly when the underlying error carried a valid one. */
|
|
395
|
+
cause?: PlaybookFailureCause;
|
|
64
396
|
}
|
|
65
397
|
|
|
66
398
|
export type PlaybookStateValue =
|
|
@@ -259,6 +591,17 @@ export interface PlaybookRepositoryObservation {
|
|
|
259
591
|
readonly projectionDigest: string;
|
|
260
592
|
}
|
|
261
593
|
|
|
594
|
+
/**
|
|
595
|
+
* The fate of the baseline projection entries a governed call absorbed into
|
|
596
|
+
* its one commit, altered, or lost (DR-062). Each list holds sorted unique
|
|
597
|
+
* baseline paths and the three are pairwise disjoint.
|
|
598
|
+
*/
|
|
599
|
+
export interface PlaybookRepositoryPreExistingChanges {
|
|
600
|
+
readonly absorbed: readonly string[];
|
|
601
|
+
readonly altered: readonly string[];
|
|
602
|
+
readonly lost: readonly string[];
|
|
603
|
+
}
|
|
604
|
+
|
|
262
605
|
/** The fail-closed classification of one complete physical or logical receipt. */
|
|
263
606
|
export interface PlaybookRepositoryReceipt {
|
|
264
607
|
readonly classification:
|
|
@@ -272,6 +615,7 @@ export interface PlaybookRepositoryReceipt {
|
|
|
272
615
|
readonly baseline: PlaybookRepositoryObservation;
|
|
273
616
|
readonly after?: PlaybookRepositoryObservation;
|
|
274
617
|
readonly commitOid?: string;
|
|
618
|
+
readonly preExisting?: PlaybookRepositoryPreExistingChanges;
|
|
275
619
|
}
|
|
276
620
|
|
|
277
621
|
/** One durably ordered physical governed-player boundary (DR-040). */
|
|
@@ -433,12 +777,25 @@ export interface PlaybookRuntimeSnapshot {
|
|
|
433
777
|
suspendedCall?: PlaybookSuspendedCall;
|
|
434
778
|
}
|
|
435
779
|
|
|
780
|
+
// DR-063 §3: what running an advertised action would do. `ready` is the only
|
|
781
|
+
// standing that can change anything; `no-op` runs and changes nothing; a
|
|
782
|
+
// `blocked` action cannot run at all. An absent standing reads as `ready`, so
|
|
783
|
+
// a runtime that publishes none advertises exactly what it always did.
|
|
784
|
+
export type PlaybookControlStanding = 'ready' | 'no-op' | 'blocked';
|
|
785
|
+
|
|
786
|
+
// DR-063 §3: the closed reason list a non-`ready` standing names.
|
|
787
|
+
export type PlaybookControlActionReason = 'receipt-complete';
|
|
788
|
+
|
|
436
789
|
// DR-029: one currently valid, runtime-advertised control action. The id
|
|
437
790
|
// is stable within the returned view; the label is runtime-written,
|
|
438
|
-
// Boss-appropriate text derived from source state descriptions.
|
|
791
|
+
// Boss-appropriate text derived from source state descriptions. DR-063 §3
|
|
792
|
+
// adds the standing, so a host never draws a control without saying what
|
|
793
|
+
// running it would do; an action that publishes none reads as `ready`.
|
|
439
794
|
export interface PlaybookControlAction {
|
|
440
795
|
id: string;
|
|
441
796
|
label: string;
|
|
797
|
+
standing?: PlaybookControlStanding;
|
|
798
|
+
reason?: PlaybookControlActionReason;
|
|
442
799
|
}
|
|
443
800
|
|
|
444
801
|
// DR-029: the sanitized control view `describe()` returns — current
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnyStateMachine, EventObject, PromiseActorLogic } from 'xstate';
|
|
2
|
-
import type { CaptainResult, JsonValue, PlaybookEffectBoundary, PlaybookEffectBoundaryStart, PlaybookEffectLedger, PlaybookEffectLedgerCapability, PlaybookPendingBossQuestion, PlaybookPorts, PlaybookRepositoryReceipt, PlaybookRuntimeFactory, PlaybookSession, PlaybookState, PlayerResult } from './runtime.js';
|
|
2
|
+
import type { CaptainResult, JsonValue, PlaybookEffectBoundary, PlaybookEffectBoundaryStart, PlaybookEffectLedger, PlaybookEffectLedgerCapability, NormalizedError, PlaybookPendingBossQuestion, PlaybookPorts, PlaybookRepositoryReceipt, PlaybookRuntimeFactory, PlaybookSession, PlaybookState, PlayerResult } from './runtime.js';
|
|
3
3
|
export interface PlaybookPendingBossQuestionContext {
|
|
4
4
|
questionId: string;
|
|
5
5
|
resumeStateId: string;
|
|
@@ -58,6 +58,12 @@ export interface RuntimeBoundaryCalls {
|
|
|
58
58
|
*/
|
|
59
59
|
takeGovernedPlayerOutput?(result: PlayerResult): GovernedPlayerSettlement | undefined;
|
|
60
60
|
recordGovernedPlayerOutput?(result: PlayerResult, output: PlaybookActorOutput): void;
|
|
61
|
+
/**
|
|
62
|
+
* DR-063 §2: decorate the failure the bridge builds for a non-`ok` result
|
|
63
|
+
* with the cause the boundary decided at the call itself, where the role,
|
|
64
|
+
* the resolved player, and the reported error are known.
|
|
65
|
+
*/
|
|
66
|
+
markPlayerResultFailure?(error: Error): Error;
|
|
61
67
|
callJudge(purpose: JudgePurpose, stateId: string | undefined, prompt: string, signal: AbortSignal): Promise<string>;
|
|
62
68
|
callCaptain?(input: PlaybookCaptainInput, prompt: string, signal: AbortSignal, callOptions?: XStateCaptainCallOptions): Promise<CaptainResult>;
|
|
63
69
|
}
|
|
@@ -377,15 +383,8 @@ export type XStatePlaybookRuntimeSpecV3<TOptions> = XStatePlaybookRuntimeSpec<TO
|
|
|
377
383
|
export declare function stripCodeFence(text: string): string;
|
|
378
384
|
export declare function extractJsonValue(text: string, start: number, repair: boolean): string | undefined;
|
|
379
385
|
export declare function parseJudgeJson(raw: string): unknown;
|
|
380
|
-
export declare function normalizeErrorCompact(err: unknown):
|
|
381
|
-
|
|
382
|
-
message: string;
|
|
383
|
-
} | undefined;
|
|
384
|
-
export declare function normalizeErrorFull(err: unknown): {
|
|
385
|
-
name: string;
|
|
386
|
-
message: string;
|
|
387
|
-
stack?: string;
|
|
388
|
-
} | undefined;
|
|
386
|
+
export declare function normalizeErrorCompact(err: unknown): Omit<NormalizedError, 'stack'> | undefined;
|
|
387
|
+
export declare function normalizeErrorFull(err: unknown): NormalizedError | undefined;
|
|
389
388
|
/** Read the FSM context's single pending Boss question, when well-formed. */
|
|
390
389
|
export declare function pendingBossQuestionFromContext(context: Record<string, unknown>): PlaybookPendingBossQuestionContext | undefined;
|
|
391
390
|
/** Add clarification context without repeating the question in a live conversation. */
|