@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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PlaybookState } from './runtime.js';
|
|
2
|
+
export declare const ACCEPTED_OUTCOME_ACTION_TYPE = "playbook.acceptedOutcome";
|
|
3
|
+
export interface AcceptedOutcomeReceipt {
|
|
4
|
+
readonly source: string;
|
|
5
|
+
readonly target: string;
|
|
6
|
+
readonly acceptedOutcome: string;
|
|
7
|
+
}
|
|
8
|
+
interface InspectedAction {
|
|
9
|
+
readonly type: string;
|
|
10
|
+
readonly params: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface AcceptedOutcomeConsumer {
|
|
13
|
+
capture(action: InspectedAction): void;
|
|
14
|
+
confirm(previousState: PlaybookState | undefined, state: PlaybookState): readonly AcceptedOutcomeReceipt[];
|
|
15
|
+
reset(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function createAcceptedOutcomeConsumer(isDeclared: (source: string, acceptedOutcome: string) => boolean): AcceptedOutcomeConsumer;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
export const ACCEPTED_OUTCOME_ACTION_TYPE = 'playbook.acceptedOutcome';
|
|
4
|
+
function exactMarkerParams(value) {
|
|
5
|
+
if (value === null ||
|
|
6
|
+
typeof value !== 'object' ||
|
|
7
|
+
Array.isArray(value) ||
|
|
8
|
+
(Object.getPrototypeOf(value) !== Object.prototype &&
|
|
9
|
+
Object.getPrototypeOf(value) !== null)) {
|
|
10
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} params must be a plain object`);
|
|
11
|
+
}
|
|
12
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
13
|
+
const keys = Reflect.ownKeys(descriptors);
|
|
14
|
+
const expected = ['acceptedOutcome', 'source', 'target'];
|
|
15
|
+
if (keys.length !== expected.length ||
|
|
16
|
+
keys.some((key) => typeof key !== 'string' || !expected.includes(key))) {
|
|
17
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} params must contain exactly source, target, and acceptedOutcome`);
|
|
18
|
+
}
|
|
19
|
+
const stringValue = (key) => {
|
|
20
|
+
const descriptor = descriptors[key];
|
|
21
|
+
if (descriptor === undefined ||
|
|
22
|
+
!Object.prototype.hasOwnProperty.call(descriptor, 'value') ||
|
|
23
|
+
descriptor.enumerable !== true ||
|
|
24
|
+
typeof descriptor.value !== 'string' ||
|
|
25
|
+
descriptor.value.trim().length === 0) {
|
|
26
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} params.${key} must be a nonempty enumerable string data property`);
|
|
27
|
+
}
|
|
28
|
+
return descriptor.value;
|
|
29
|
+
};
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
source: stringValue('source'),
|
|
32
|
+
target: stringValue('target'),
|
|
33
|
+
acceptedOutcome: stringValue('acceptedOutcome'),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function createAcceptedOutcomeConsumer(isDeclared) {
|
|
37
|
+
let pending = [];
|
|
38
|
+
let invalidBatch = false;
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
capture(action) {
|
|
41
|
+
if (action.type !== ACCEPTED_OUTCOME_ACTION_TYPE) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (invalidBatch)
|
|
45
|
+
return;
|
|
46
|
+
try {
|
|
47
|
+
const marker = exactMarkerParams(action.params);
|
|
48
|
+
if (!isDeclared(marker.source, marker.acceptedOutcome)) {
|
|
49
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} names undeclared outcome ` +
|
|
50
|
+
`${marker.source}.${marker.acceptedOutcome}`);
|
|
51
|
+
}
|
|
52
|
+
pending.push(marker);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
pending = [];
|
|
56
|
+
invalidBatch = true;
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
confirm(previousState, state) {
|
|
61
|
+
if (invalidBatch) {
|
|
62
|
+
pending = [];
|
|
63
|
+
invalidBatch = false;
|
|
64
|
+
return Object.freeze([]);
|
|
65
|
+
}
|
|
66
|
+
if (pending.length === 0)
|
|
67
|
+
return Object.freeze([]);
|
|
68
|
+
const captured = pending;
|
|
69
|
+
pending = [];
|
|
70
|
+
if (previousState === undefined) {
|
|
71
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} marker has no prior public root snapshot`);
|
|
72
|
+
}
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const marker of captured) {
|
|
75
|
+
const identity = marker.source;
|
|
76
|
+
if (seen.has(identity)) {
|
|
77
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} source ${marker.source} was instrumented more than once in one action batch`);
|
|
78
|
+
}
|
|
79
|
+
seen.add(identity);
|
|
80
|
+
if (!previousState.activeStateIds.includes(marker.source)) {
|
|
81
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} source ${marker.source} was not confirmed by the prior public root snapshot`);
|
|
82
|
+
}
|
|
83
|
+
if (!state.activeStateIds.includes(marker.target)) {
|
|
84
|
+
throw new TypeError(`${ACCEPTED_OUTCOME_ACTION_TYPE} target ${marker.target} was not confirmed by the public root snapshot`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return Object.freeze(captured);
|
|
88
|
+
},
|
|
89
|
+
reset() {
|
|
90
|
+
pending = [];
|
|
91
|
+
invalidBatch = false;
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
+
|
|
4
|
+
import type { PlaybookState } from './runtime.js';
|
|
5
|
+
|
|
6
|
+
export const ACCEPTED_OUTCOME_ACTION_TYPE = 'playbook.acceptedOutcome';
|
|
7
|
+
|
|
8
|
+
export interface AcceptedOutcomeReceipt {
|
|
9
|
+
readonly source: string;
|
|
10
|
+
readonly target: string;
|
|
11
|
+
readonly acceptedOutcome: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface InspectedAction {
|
|
15
|
+
readonly type: string;
|
|
16
|
+
readonly params: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AcceptedOutcomeConsumer {
|
|
20
|
+
capture(action: InspectedAction): void;
|
|
21
|
+
confirm(
|
|
22
|
+
previousState: PlaybookState | undefined,
|
|
23
|
+
state: PlaybookState,
|
|
24
|
+
): readonly AcceptedOutcomeReceipt[];
|
|
25
|
+
reset(): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function exactMarkerParams(value: unknown): AcceptedOutcomeReceipt {
|
|
29
|
+
if (
|
|
30
|
+
value === null ||
|
|
31
|
+
typeof value !== 'object' ||
|
|
32
|
+
Array.isArray(value) ||
|
|
33
|
+
(Object.getPrototypeOf(value) !== Object.prototype &&
|
|
34
|
+
Object.getPrototypeOf(value) !== null)
|
|
35
|
+
) {
|
|
36
|
+
throw new TypeError(
|
|
37
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} params must be a plain object`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
41
|
+
const keys = Reflect.ownKeys(descriptors);
|
|
42
|
+
const expected = ['acceptedOutcome', 'source', 'target'];
|
|
43
|
+
if (
|
|
44
|
+
keys.length !== expected.length ||
|
|
45
|
+
keys.some((key) => typeof key !== 'string' || !expected.includes(key))
|
|
46
|
+
) {
|
|
47
|
+
throw new TypeError(
|
|
48
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} params must contain exactly source, target, and acceptedOutcome`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const stringValue = (key: string): string => {
|
|
52
|
+
const descriptor = descriptors[key];
|
|
53
|
+
if (
|
|
54
|
+
descriptor === undefined ||
|
|
55
|
+
!Object.prototype.hasOwnProperty.call(descriptor, 'value') ||
|
|
56
|
+
descriptor.enumerable !== true ||
|
|
57
|
+
typeof descriptor.value !== 'string' ||
|
|
58
|
+
descriptor.value.trim().length === 0
|
|
59
|
+
) {
|
|
60
|
+
throw new TypeError(
|
|
61
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} params.${key} must be a nonempty enumerable string data property`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return descriptor.value;
|
|
65
|
+
};
|
|
66
|
+
return Object.freeze({
|
|
67
|
+
source: stringValue('source'),
|
|
68
|
+
target: stringValue('target'),
|
|
69
|
+
acceptedOutcome: stringValue('acceptedOutcome'),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function createAcceptedOutcomeConsumer(
|
|
74
|
+
isDeclared: (source: string, acceptedOutcome: string) => boolean,
|
|
75
|
+
): AcceptedOutcomeConsumer {
|
|
76
|
+
let pending: AcceptedOutcomeReceipt[] = [];
|
|
77
|
+
let invalidBatch = false;
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
capture(action: InspectedAction) {
|
|
80
|
+
if (action.type !== ACCEPTED_OUTCOME_ACTION_TYPE) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (invalidBatch) return;
|
|
84
|
+
try {
|
|
85
|
+
const marker = exactMarkerParams(action.params);
|
|
86
|
+
if (!isDeclared(marker.source, marker.acceptedOutcome)) {
|
|
87
|
+
throw new TypeError(
|
|
88
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} names undeclared outcome ` +
|
|
89
|
+
`${marker.source}.${marker.acceptedOutcome}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
pending.push(marker);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
pending = [];
|
|
95
|
+
invalidBatch = true;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
confirm(previousState: PlaybookState | undefined, state: PlaybookState) {
|
|
100
|
+
if (invalidBatch) {
|
|
101
|
+
pending = [];
|
|
102
|
+
invalidBatch = false;
|
|
103
|
+
return Object.freeze([]);
|
|
104
|
+
}
|
|
105
|
+
if (pending.length === 0) return Object.freeze([]);
|
|
106
|
+
const captured = pending;
|
|
107
|
+
pending = [];
|
|
108
|
+
if (previousState === undefined) {
|
|
109
|
+
throw new TypeError(
|
|
110
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} marker has no prior public root snapshot`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const seen = new Set<string>();
|
|
114
|
+
for (const marker of captured) {
|
|
115
|
+
const identity = marker.source;
|
|
116
|
+
if (seen.has(identity)) {
|
|
117
|
+
throw new TypeError(
|
|
118
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} source ${marker.source} was instrumented more than once in one action batch`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
seen.add(identity);
|
|
122
|
+
if (!previousState.activeStateIds.includes(marker.source)) {
|
|
123
|
+
throw new TypeError(
|
|
124
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} source ${marker.source} was not confirmed by the prior public root snapshot`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (!state.activeStateIds.includes(marker.target)) {
|
|
128
|
+
throw new TypeError(
|
|
129
|
+
`${ACCEPTED_OUTCOME_ACTION_TYPE} target ${marker.target} was not confirmed by the public root snapshot`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return Object.freeze(captured);
|
|
134
|
+
},
|
|
135
|
+
reset() {
|
|
136
|
+
pending = [];
|
|
137
|
+
invalidBatch = false;
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
package/src/runtime.d.ts
CHANGED
|
@@ -55,6 +55,7 @@ export interface PlaybookSuspendedCall extends PlaybookPendingCall {
|
|
|
55
55
|
stateId: string;
|
|
56
56
|
text: string;
|
|
57
57
|
turnId?: number;
|
|
58
|
+
effectBoundaryPrefixSequence?: number | null;
|
|
58
59
|
}
|
|
59
60
|
export interface PlaybookCallRequest {
|
|
60
61
|
callId: string;
|
|
@@ -90,6 +91,9 @@ export type PlaybookCallStart = {
|
|
|
90
91
|
export type PlaybookRunResult = {
|
|
91
92
|
outcome: 'quiescent' | 'no-action';
|
|
92
93
|
state: PlaybookState;
|
|
94
|
+
} | {
|
|
95
|
+
outcome: 'unresolved-effect';
|
|
96
|
+
state: PlaybookState;
|
|
93
97
|
} | {
|
|
94
98
|
outcome: 'failed' | 'aborted';
|
|
95
99
|
state: PlaybookState;
|
|
@@ -97,6 +101,7 @@ export type PlaybookRunResult = {
|
|
|
97
101
|
} | {
|
|
98
102
|
outcome: 'terminal';
|
|
99
103
|
state: PlaybookState;
|
|
104
|
+
stateDescription?: string;
|
|
100
105
|
output?: JsonValue;
|
|
101
106
|
} | {
|
|
102
107
|
outcome: 'suspended';
|
|
@@ -125,9 +130,14 @@ export interface PlaybookSession {
|
|
|
125
130
|
playerSessions?: PlayerSessionStore;
|
|
126
131
|
ports: PlaybookPorts;
|
|
127
132
|
}
|
|
128
|
-
export
|
|
133
|
+
export interface PlaybookAdoptionContext {
|
|
134
|
+
readonly sourceSessionId: string;
|
|
135
|
+
readonly sourceGenerationId: string;
|
|
136
|
+
readonly targetChildSessionId?: string;
|
|
137
|
+
}
|
|
138
|
+
export type PlaybookTraceType = 'session.started' | 'boss.input.received' | 'judge.call.started' | 'judge.call.finished' | 'player.call.started' | 'player.call.finished' | 'captain.call.started' | 'captain.call.finished' | 'playbook.call.started' | 'playbook.call.finished' | 'apply.started' | 'apply.finished' | 'fsm.transition' | 'outcome.accepted' | 'status.emitted' | 'boss.input.settled' | 'session.disposed';
|
|
129
139
|
export interface PlaybookTraceEvent {
|
|
130
|
-
schemaVersion:
|
|
140
|
+
schemaVersion: 4;
|
|
131
141
|
sessionId: string;
|
|
132
142
|
playbookId: string;
|
|
133
143
|
rootSessionId: string;
|
|
@@ -152,8 +162,127 @@ export interface PlaybookPendingBossQuestion {
|
|
|
152
162
|
question: string;
|
|
153
163
|
sourceItem?: string;
|
|
154
164
|
}
|
|
165
|
+
/** One repository disposition declared by a governed outcome arm (DR-040). */
|
|
166
|
+
export type PlaybookRepositoryDisposition = 'unchanged' | 'one-descendant-commit' | 'deferred';
|
|
167
|
+
/** A detached Git-visible repository observation owned by the effect ledger. */
|
|
168
|
+
export interface PlaybookRepositoryObservation {
|
|
169
|
+
readonly worktree: string;
|
|
170
|
+
readonly gitDir: string;
|
|
171
|
+
readonly head: string;
|
|
172
|
+
readonly projection: Readonly<Record<string, JsonValue>>;
|
|
173
|
+
readonly projectionDigest: string;
|
|
174
|
+
}
|
|
175
|
+
/** The fail-closed classification of one complete physical or logical receipt. */
|
|
176
|
+
export interface PlaybookRepositoryReceipt {
|
|
177
|
+
readonly classification: 'unchanged' | 'one-descendant-commit' | 'multiple-commits' | 'rewritten-or-non-descendant' | 'worktree-only-change' | 'concurrent-or-foreign-change' | 'observation-ambiguous';
|
|
178
|
+
readonly baseline: PlaybookRepositoryObservation;
|
|
179
|
+
readonly after?: PlaybookRepositoryObservation;
|
|
180
|
+
readonly commitOid?: string;
|
|
181
|
+
}
|
|
182
|
+
/** One durably ordered physical governed-player boundary (DR-040). */
|
|
183
|
+
export interface PlaybookEffectBoundary {
|
|
184
|
+
readonly sequence: number;
|
|
185
|
+
readonly boundaryId: string;
|
|
186
|
+
readonly attemptId: string;
|
|
187
|
+
readonly attemptNumber: number;
|
|
188
|
+
readonly playbookId: string;
|
|
189
|
+
readonly runtimeSessionId: string;
|
|
190
|
+
readonly turnId: number;
|
|
191
|
+
readonly callId: string;
|
|
192
|
+
readonly roleId: string;
|
|
193
|
+
readonly sourceStateId: string;
|
|
194
|
+
readonly sourceOutcomeSchema: JsonValue;
|
|
195
|
+
readonly dispositions: readonly PlaybookRepositoryDisposition[];
|
|
196
|
+
readonly canonicalWorktree: {
|
|
197
|
+
readonly worktree: string;
|
|
198
|
+
readonly gitDir: string;
|
|
199
|
+
};
|
|
200
|
+
readonly baseline: PlaybookRepositoryObservation;
|
|
201
|
+
readonly after?: PlaybookRepositoryObservation;
|
|
202
|
+
readonly physicalReceipt?: PlaybookRepositoryReceipt;
|
|
203
|
+
readonly finalText?: string;
|
|
204
|
+
readonly semanticCandidate?: JsonValue;
|
|
205
|
+
readonly initialSemanticCandidate?: JsonValue;
|
|
206
|
+
readonly correctionBudget: {
|
|
207
|
+
readonly limit: 1;
|
|
208
|
+
readonly spent: boolean;
|
|
209
|
+
};
|
|
210
|
+
readonly cohortId?: string;
|
|
211
|
+
readonly logicalOperationId?: string;
|
|
212
|
+
}
|
|
213
|
+
/** One physical boundary before the host assigns attempt and sequence data. */
|
|
214
|
+
export type PlaybookEffectBoundaryStart = Omit<PlaybookEffectBoundary, 'sequence' | 'attemptId' | 'attemptNumber' | 'after' | 'physicalReceipt' | 'finalText' | 'semanticCandidate' | 'initialSemanticCandidate'>;
|
|
215
|
+
/** One deferred logical operation spanning its ordered physical boundaries. */
|
|
216
|
+
export interface PlaybookEffectLogicalOperation {
|
|
217
|
+
readonly sequence: number;
|
|
218
|
+
readonly operationId: string;
|
|
219
|
+
readonly playbookId: string;
|
|
220
|
+
readonly runtimeSessionId: string;
|
|
221
|
+
readonly boundaryIds: readonly string[];
|
|
222
|
+
readonly originalBaseline: PlaybookRepositoryObservation;
|
|
223
|
+
readonly checkpoint?: PlaybookRepositoryObservation;
|
|
224
|
+
readonly pendingQuestion?: PlaybookPendingBossQuestion;
|
|
225
|
+
readonly playerContinuation?: JsonValue;
|
|
226
|
+
readonly checkpointRestorationEligible: boolean;
|
|
227
|
+
readonly logicalReceipt?: PlaybookRepositoryReceipt;
|
|
228
|
+
}
|
|
229
|
+
/** Complete detached mirror of one host-owned reconciliation ledger. */
|
|
230
|
+
export interface PlaybookEffectLedger {
|
|
231
|
+
readonly schemaVersion: 1;
|
|
232
|
+
readonly revision: number;
|
|
233
|
+
readonly boundaries: readonly PlaybookEffectBoundary[];
|
|
234
|
+
readonly logicalOperations: readonly PlaybookEffectLogicalOperation[];
|
|
235
|
+
}
|
|
236
|
+
/** One mutation accepted by the host-owned effect-ledger write-ahead boundary. */
|
|
237
|
+
export type PlaybookEffectLedgerCommand = {
|
|
238
|
+
readonly kind: 'start-boundaries';
|
|
239
|
+
readonly boundaries: readonly [
|
|
240
|
+
PlaybookEffectBoundaryStart,
|
|
241
|
+
...PlaybookEffectBoundaryStart[]
|
|
242
|
+
];
|
|
243
|
+
} | {
|
|
244
|
+
readonly kind: 'replace-boundaries';
|
|
245
|
+
readonly replacements: readonly [
|
|
246
|
+
{
|
|
247
|
+
readonly expected: PlaybookEffectBoundary;
|
|
248
|
+
readonly next: PlaybookEffectBoundary;
|
|
249
|
+
},
|
|
250
|
+
...{
|
|
251
|
+
readonly expected: PlaybookEffectBoundary;
|
|
252
|
+
readonly next: PlaybookEffectBoundary;
|
|
253
|
+
}[]
|
|
254
|
+
];
|
|
255
|
+
} | {
|
|
256
|
+
readonly kind: 'append-logical-operations';
|
|
257
|
+
readonly operations: readonly [
|
|
258
|
+
Omit<PlaybookEffectLogicalOperation, 'sequence'>,
|
|
259
|
+
...Omit<PlaybookEffectLogicalOperation, 'sequence'>[]
|
|
260
|
+
];
|
|
261
|
+
} | {
|
|
262
|
+
readonly kind: 'replace-logical-operations';
|
|
263
|
+
readonly replacements: readonly [
|
|
264
|
+
{
|
|
265
|
+
readonly expected: PlaybookEffectLogicalOperation;
|
|
266
|
+
readonly next: PlaybookEffectLogicalOperation;
|
|
267
|
+
},
|
|
268
|
+
...{
|
|
269
|
+
readonly expected: PlaybookEffectLogicalOperation;
|
|
270
|
+
readonly next: PlaybookEffectLogicalOperation;
|
|
271
|
+
}[]
|
|
272
|
+
];
|
|
273
|
+
};
|
|
274
|
+
/** A nonempty command batch persisted as one ledger revision. */
|
|
275
|
+
export type PlaybookEffectLedgerCommandBatch = readonly [
|
|
276
|
+
PlaybookEffectLedgerCommand,
|
|
277
|
+
...PlaybookEffectLedgerCommand[]
|
|
278
|
+
];
|
|
279
|
+
/** Live current-host seam for atomic effect-ledger observation and mutation. */
|
|
280
|
+
export interface PlaybookEffectLedgerCapability {
|
|
281
|
+
snapshot(): PlaybookEffectLedger;
|
|
282
|
+
writeAhead(commands: PlaybookEffectLedgerCommandBatch): Promise<PlaybookEffectLedger>;
|
|
283
|
+
}
|
|
155
284
|
export interface PlaybookRuntimeSnapshot {
|
|
156
|
-
schemaVersion:
|
|
285
|
+
schemaVersion: 4;
|
|
157
286
|
playbookId: string;
|
|
158
287
|
machine: JsonValue;
|
|
159
288
|
roleResumeTokens: {
|
|
@@ -169,6 +298,21 @@ export interface PlaybookRuntimeSnapshot {
|
|
|
169
298
|
};
|
|
170
299
|
state: PlaybookState;
|
|
171
300
|
pendingBossQuestions: readonly PlaybookPendingBossQuestion[];
|
|
301
|
+
effectLedger: PlaybookEffectLedger;
|
|
302
|
+
/** Original runtime identity retained across schema-3 adoption lineage. */
|
|
303
|
+
retainedEffectSourceSessionId?: string;
|
|
304
|
+
/**
|
|
305
|
+
* Unsafe retained-adoption checkpoint. The marker remains durable until
|
|
306
|
+
* authoritative reconciliation proves its complete suffix replay-safe.
|
|
307
|
+
*/
|
|
308
|
+
retainedEffectReconciliation?: {
|
|
309
|
+
readonly sourceSessionId: string;
|
|
310
|
+
readonly checkpoint: PlaybookEffectLedger;
|
|
311
|
+
};
|
|
312
|
+
failedEffectAttempt?: {
|
|
313
|
+
readonly boundaryPrefix: number;
|
|
314
|
+
readonly attemptId: string | null;
|
|
315
|
+
};
|
|
172
316
|
suspendedCall?: PlaybookSuspendedCall;
|
|
173
317
|
}
|
|
174
318
|
export interface PlaybookControlAction {
|
|
@@ -193,11 +337,29 @@ export type PlaybookControlReceipt = {
|
|
|
193
337
|
disposition: 'failed';
|
|
194
338
|
error: NormalizedError;
|
|
195
339
|
};
|
|
340
|
+
export interface PlaybookRetainedGenerationMetadata {
|
|
341
|
+
readonly unfinishedFinalStateIds: readonly string[];
|
|
342
|
+
}
|
|
196
343
|
export interface PlaybookRuntime {
|
|
197
344
|
init(session: PlaybookSession): Promise<void>;
|
|
198
345
|
exportSnapshot?(): PlaybookRuntimeSnapshot | undefined;
|
|
199
346
|
restore?(session: PlaybookSession, snapshot: PlaybookRuntimeSnapshot): Promise<void>;
|
|
347
|
+
adopt?(session: PlaybookSession, snapshot: PlaybookRuntimeSnapshot, context: PlaybookAdoptionContext): Promise<void>;
|
|
348
|
+
readonly retainedGenerationMetadata?: PlaybookRetainedGenerationMetadata;
|
|
200
349
|
describe?(): PlaybookControlView;
|
|
350
|
+
/**
|
|
351
|
+
* Host-only identities of the durable envelopes that still require
|
|
352
|
+
* unresolved-effect settlement. The host owns their bounded projection
|
|
353
|
+
* from its authoritative effect ledger; no repository evidence enters a
|
|
354
|
+
* runtime-owned run result.
|
|
355
|
+
*/
|
|
356
|
+
unresolvedEffectEnvelopes?(): readonly ({
|
|
357
|
+
readonly kind: 'boundary';
|
|
358
|
+
readonly boundaryId: string;
|
|
359
|
+
} | {
|
|
360
|
+
readonly kind: 'logical-operation';
|
|
361
|
+
readonly operationId: string;
|
|
362
|
+
})[];
|
|
201
363
|
apply?(input: {
|
|
202
364
|
actionId: string;
|
|
203
365
|
key: string;
|