@sublang/playbook 15.0.0 → 16.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/embedding.md +9 -7
- package/package.json +3 -1
- package/reference/sdlc/branch.playbook/branch.fsm.d.ts +63 -33
- package/reference/sdlc/branch.playbook/branch.fsm.js +190 -108
- package/reference/sdlc/branch.playbook/branch.fsm.ts +269 -147
- package/reference/sdlc/branch.playbook/branch.gears.md +7 -17
- package/reference/sdlc/branch.playbook/branch.playbook.d.ts +12 -16
- package/reference/sdlc/branch.playbook/branch.playbook.js +112 -101
- package/reference/sdlc/branch.playbook/branch.playbook.ts +128 -171
- package/reference/sdlc/branch.playbook/branch.registry.d.ts +3 -2
- package/reference/sdlc/branch.playbook/branch.registry.ts +6 -2
- package/reference/sdlc/code.playbook/bin/repository-effects.js +54 -13
- package/reference/sdlc/code.playbook/code.fsm.d.ts +143 -90
- package/reference/sdlc/code.playbook/code.fsm.introspect.d.ts +8 -8
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +13 -13
- package/reference/sdlc/code.playbook/code.fsm.js +609 -649
- package/reference/sdlc/code.playbook/code.fsm.ts +890 -821
- package/reference/sdlc/code.playbook/code.gears.md +39 -29
- package/reference/sdlc/code.playbook/code.playbook.d.ts +13 -16
- package/reference/sdlc/code.playbook/code.playbook.js +166 -147
- package/reference/sdlc/code.playbook/code.playbook.ts +180 -229
- package/reference/sdlc/code.playbook/code.registry.d.ts +3 -2
- package/reference/sdlc/code.playbook/code.registry.ts +6 -2
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +42 -0
- package/reference/sdlc/code.playbook/playbook-captain.js +829 -194
- package/reference/sdlc/code.playbook/playbook-captain.ts +1227 -220
- package/reference/sdlc/decide.md +1 -0
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +270 -154
- package/reference/sdlc/decide.playbook/decide.fsm.js +765 -732
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1167 -945
- package/reference/sdlc/decide.playbook/decide.gears.md +28 -21
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +11 -67
- package/reference/sdlc/decide.playbook/decide.playbook.js +155 -3570
- package/reference/sdlc/decide.playbook/decide.playbook.ts +186 -5061
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +8 -4
- package/reference/sdlc/decide.playbook/decide.registry.js +2 -2
- package/reference/sdlc/decide.playbook/decide.registry.ts +13 -6
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +113 -131
- package/reference/sdlc/dev.playbook/dev.fsm.js +517 -575
- package/reference/sdlc/dev.playbook/dev.fsm.ts +667 -787
- package/reference/sdlc/dev.playbook/dev.gears.md +52 -54
- package/reference/sdlc/dev.playbook/dev.playbook.d.ts +13 -10
- package/reference/sdlc/dev.playbook/dev.playbook.js +128 -120
- package/reference/sdlc/dev.playbook/dev.playbook.ts +143 -193
- package/reference/sdlc/dev.playbook/dev.registry.d.ts +3 -2
- package/reference/sdlc/dev.playbook/dev.registry.ts +6 -2
- package/reference/sdlc/pr.playbook/pr.fsm.d.ts +92 -85
- package/reference/sdlc/pr.playbook/pr.fsm.js +461 -518
- package/reference/sdlc/pr.playbook/pr.fsm.ts +533 -663
- package/reference/sdlc/pr.playbook/pr.gears.md +47 -63
- package/reference/sdlc/pr.playbook/pr.playbook.d.ts +13 -23
- package/reference/sdlc/pr.playbook/pr.playbook.js +118 -112
- package/reference/sdlc/pr.playbook/pr.playbook.ts +136 -190
- package/reference/sdlc/pr.playbook/pr.registry.d.ts +3 -2
- package/reference/sdlc/pr.playbook/pr.registry.ts +4 -2
- package/reference/sdlc/review.md +3 -3
- package/reference/sdlc/review.playbook/review.fsm.d.ts +150 -116
- package/reference/sdlc/review.playbook/review.fsm.js +479 -550
- package/reference/sdlc/review.playbook/review.fsm.ts +717 -662
- package/reference/sdlc/review.playbook/review.gears.md +32 -29
- package/reference/sdlc/review.playbook/review.playbook.d.ts +12 -12
- package/reference/sdlc/review.playbook/review.playbook.js +180 -155
- package/reference/sdlc/review.playbook/review.playbook.ts +190 -230
- package/reference/sdlc/review.playbook/review.registry.d.ts +6 -5
- package/reference/sdlc/review.playbook/review.registry.js +3 -3
- package/reference/sdlc/review.playbook/review.registry.ts +9 -5
- package/slc/gears2fsm.md +72 -3
- package/slc/link.md +50 -27
- package/slc/materialize-link.mjs +51 -13
- package/slc/optimize.md +4 -1
- package/slc/prefix-prompts.mjs +191 -0
- package/slc/prefix.md +203 -0
- package/slc/slc.pin-inputs.json +11 -1
- package/slc/text2gears.md +71 -11
- package/src/xstate-playbook-runtime.d.ts +82 -9
- package/src/xstate-playbook-runtime.js +1224 -283
- package/src/xstate-playbook-runtime.ts +1671 -338
- package/src/xstate-runtime.d.ts +3 -1
- package/src/xstate-runtime.js +9 -2
- package/src/xstate-runtime.ts +12 -2
|
@@ -1,217 +1,174 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Boss event: deterministic START_BRANCH entry; exact Boss text becomes
|
|
8
|
-
// callerInput; pending Coder questions retain BOSS_REPLY
|
|
9
|
-
// Adjudication: LLM judge for the one Coder state; coderOutput is carried
|
|
10
|
-
// verbatim; baseRevision is receipt-owned effect evidence —
|
|
11
|
-
// the unchanged receipt's observed HEAD — never taken from
|
|
12
|
-
// Coder's prose
|
|
13
|
-
// Compat: artifact schema 3 / runtime ABI 1
|
|
14
|
-
|
|
3
|
+
// Generated by slc/materialize-link.mjs; governed by the complete slc/link.md.
|
|
4
|
+
// FSM: "./branch.fsm.js"; export: branchMachine; strategies: shared defaults with labelled string relays and nested actors.
|
|
5
|
+
import { branchMachine as machine } from "./branch.fsm.js";
|
|
6
|
+
import type { PlayerInput as SourcePlayerInput } from "./branch.fsm.js";
|
|
15
7
|
import {
|
|
16
|
-
createXStatePlaybookRuntime,
|
|
17
|
-
composePlayerContinuation,
|
|
18
|
-
snapshotJsonValue,
|
|
8
|
+
createXStatePlaybookRuntime, snapshotJsonValue,
|
|
9
|
+
composePlayerContinuation, type XStatePromptIdentity,
|
|
19
10
|
type PlaybookPlayerInput,
|
|
20
|
-
type XStatePlaybookRuntimeFactory,
|
|
21
11
|
type XStatePlaybookRuntimeConstruction,
|
|
12
|
+
type XStatePlaybookRuntimeFactory,
|
|
22
13
|
type XStatePlaybookRuntimeSpecV3,
|
|
23
14
|
} from '@sublang/playbook/xstate-runtime';
|
|
24
|
-
|
|
25
|
-
branchMachine,
|
|
26
|
-
type BranchInput,
|
|
27
|
-
type PlayerInput,
|
|
28
|
-
} from './branch.fsm.js';
|
|
29
|
-
import type { PlaybookHostConstructionCapabilities } from '../code.playbook/playbook-captain.js';
|
|
30
|
-
import type {
|
|
31
|
-
CaptainCallOptions,
|
|
32
|
-
CaptainResult,
|
|
33
|
-
JsonValue,
|
|
34
|
-
NormalizedError,
|
|
35
|
-
PlayerCallOptions,
|
|
36
|
-
PlaybookCallRequest,
|
|
37
|
-
PlaybookCallResult,
|
|
38
|
-
PlaybookCallStart,
|
|
39
|
-
PlaybookControlReceipt,
|
|
40
|
-
PlaybookControlView,
|
|
41
|
-
PlaybookPendingCall,
|
|
42
|
-
PlaybookPorts,
|
|
43
|
-
PlaybookRunResult,
|
|
44
|
-
PlaybookRuntime,
|
|
45
|
-
PlaybookRuntimeFactory,
|
|
46
|
-
PlaybookRuntimeSnapshot,
|
|
47
|
-
PlaybookSession,
|
|
48
|
-
PlaybookState,
|
|
49
|
-
PlaybookStateValue,
|
|
50
|
-
PlaybookTraceEvent,
|
|
51
|
-
PlaybookTraceType,
|
|
15
|
+
export type {
|
|
52
16
|
PlayerResult,
|
|
17
|
+
PlayerCallOptions,
|
|
53
18
|
PlayerSessionStore,
|
|
54
|
-
} from '@sublang/playbook/runtime';
|
|
55
|
-
|
|
56
|
-
export type {
|
|
57
|
-
CaptainCallOptions,
|
|
58
19
|
CaptainResult,
|
|
20
|
+
CaptainCallOptions,
|
|
59
21
|
JsonValue,
|
|
60
22
|
NormalizedError,
|
|
61
|
-
PlayerCallOptions,
|
|
62
23
|
PlaybookCallRequest,
|
|
63
24
|
PlaybookCallResult,
|
|
64
25
|
PlaybookCallStart,
|
|
65
|
-
|
|
66
|
-
|
|
26
|
+
PlaybookStateValue,
|
|
27
|
+
PlaybookState,
|
|
67
28
|
PlaybookPendingCall,
|
|
68
|
-
PlaybookPorts,
|
|
69
29
|
PlaybookRunResult,
|
|
70
30
|
PlaybookRuntime,
|
|
71
31
|
PlaybookRuntimeFactory,
|
|
72
32
|
PlaybookRuntimeSnapshot,
|
|
73
33
|
PlaybookSession,
|
|
74
|
-
|
|
75
|
-
PlaybookStateValue,
|
|
34
|
+
PlaybookPorts,
|
|
76
35
|
PlaybookTraceEvent,
|
|
77
36
|
PlaybookTraceType,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
};
|
|
37
|
+
PlaybookControlReceipt,
|
|
38
|
+
PlaybookControlView,
|
|
39
|
+
} from '@sublang/playbook/runtime';
|
|
81
40
|
|
|
82
|
-
export
|
|
83
|
-
export type BranchPlaybookHostCapabilities =
|
|
84
|
-
PlaybookHostConstructionCapabilities &
|
|
85
|
-
XStatePlaybookRuntimeConstruction<BranchPlaybookOptions, object>['hostCapabilities'];
|
|
41
|
+
export interface PlaybookRuntimeOptions {
|
|
86
42
|
|
|
87
|
-
|
|
43
|
+
}
|
|
44
|
+
export type PlaybookHostCapabilities = XStatePlaybookRuntimeConstruction<
|
|
45
|
+
PlaybookRuntimeOptions, { readonly authority: object }
|
|
46
|
+
>['hostCapabilities'];
|
|
88
47
|
|
|
48
|
+
const OPTION_SCHEMA = {} as const;
|
|
49
|
+
const INPUT_MAPPING = {} as const;
|
|
50
|
+
const RESUMABLE_STATE_IDS: ReadonlySet<string> = new Set([
|
|
51
|
+
"createBranch"
|
|
52
|
+
]);
|
|
53
|
+
const UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string> = new Set([
|
|
54
|
+
"refused"
|
|
55
|
+
]);
|
|
89
56
|
const VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string> = new Set([
|
|
90
|
-
|
|
57
|
+
"coderOutput"
|
|
91
58
|
]);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
captured === null ||
|
|
98
|
-
typeof captured !== 'object' ||
|
|
99
|
-
Array.isArray(captured)
|
|
100
|
-
) {
|
|
101
|
-
throw new TypeError('BRANCH runtime options must be an object');
|
|
59
|
+
export function validateOptions(value: unknown): PlaybookRuntimeOptions {
|
|
60
|
+
const captured = snapshotJsonValue(value === undefined ? {} : value, "BRANCH runtime options");
|
|
61
|
+
if (captured === null || typeof captured !== 'object' || Array.isArray(captured)) {
|
|
62
|
+
throw new TypeError("BRANCH runtime options must be an object");
|
|
102
63
|
}
|
|
103
|
-
const
|
|
104
|
-
for (const key of Object.keys(
|
|
105
|
-
throw new TypeError(
|
|
64
|
+
const fields = captured as Readonly<Record<string, unknown>>;
|
|
65
|
+
for (const key of Object.keys(captured)) {
|
|
66
|
+
if (!Object.hasOwn(OPTION_SCHEMA, key)) throw new TypeError("BRANCH runtime options." + key + ' is not declared');
|
|
106
67
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function quotedContinuation(value: string): string {
|
|
118
|
-
return value.replaceAll('\n', '\n> ');
|
|
68
|
+
for (const [key, schema] of Object.entries(OPTION_SCHEMA) as [string, { type: string; required: boolean }][]) {
|
|
69
|
+
if (!Object.hasOwn(captured, key)) {
|
|
70
|
+
if (schema.required) throw new TypeError("BRANCH runtime options." + key + ' is required');
|
|
71
|
+
} else if (typeof fields[key] !== schema.type || (schema.type === 'number' && !Number.isFinite(fields[key]))) {
|
|
72
|
+
throw new TypeError("BRANCH runtime options." + key + ' must be a ' + schema.type);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return captured as unknown as PlaybookRuntimeOptions;
|
|
119
76
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
(
|
|
131
|
-
const
|
|
77
|
+
function renderPlayerPrompt(input: SourcePlayerInput, promptIdentity: XStatePromptIdentity, resuming = false): string {
|
|
78
|
+
const fields = input as unknown as Readonly<Record<string, unknown>>;
|
|
79
|
+
const mapping: Readonly<Record<string, string>> = {};
|
|
80
|
+
const identities: Readonly<Record<string, string>> = {};
|
|
81
|
+
const omitted = new Set<string>([]);
|
|
82
|
+
const fieldFor = (token: string): string => Object.hasOwn(mapping, token) ? mapping[token]! : (token === '#' ? 'irNumber' : token.replace(/-([A-Za-z0-9_$])/g, (_match: string, next: string) => next.toUpperCase()));
|
|
83
|
+
const parts = input.prompt.split(/(\r?\n)/);
|
|
84
|
+
let body = '';
|
|
85
|
+
for (let index = 0; index < parts.length; index += 2) {
|
|
86
|
+
const line = parts[index]!;
|
|
87
|
+
if (omitted.has(line)) {
|
|
88
|
+
const token = /<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>$/.exec(line)![1]!;
|
|
89
|
+
if (fields[fieldFor(token)] === '') continue;
|
|
90
|
+
}
|
|
91
|
+
body += line.replace(/<(#|[A-Za-z_$][A-Za-z0-9_$-]*)>/g, (match: string, token: string): string => {
|
|
92
|
+
const value = Object.hasOwn(identities, token) ? promptIdentity(identities[token]!) : fields[fieldFor(token)];
|
|
132
93
|
if (typeof value !== 'string') return match;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
},
|
|
137
|
-
);
|
|
94
|
+
return line.startsWith('> ') ? value.replace(/\r?\n/g, (ending: string) => ending + '> ') : value;
|
|
95
|
+
}) + (parts[index + 1] ?? '');
|
|
96
|
+
}
|
|
138
97
|
return composePlayerContinuation(input, body, resuming);
|
|
139
98
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
composePlayerPrompt,
|
|
143
|
-
VERBATIM_PAYLOAD_FIELDS,
|
|
144
|
-
UNFINISHED_FINAL_STATE_IDS,
|
|
145
|
-
};
|
|
146
|
-
|
|
99
|
+
const composePlayerPrompt = (input: PlaybookPlayerInput, promptIdentity: XStatePromptIdentity, resuming?: boolean): string =>
|
|
100
|
+
renderPlayerPrompt(input as SourcePlayerInput, promptIdentity, resuming);
|
|
147
101
|
const runtimeSpec = {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
entryEvent: {
|
|
156
|
-
type:
|
|
157
|
-
textField:
|
|
158
|
-
|
|
159
|
-
// reads it back from the persisted machine snapshot and survives a
|
|
160
|
-
// continued session (DR-034).
|
|
161
|
-
contextField: 'callerInput',
|
|
102
|
+
composePlayerPrompt,
|
|
103
|
+
...{
|
|
104
|
+
"label": "BRANCH",
|
|
105
|
+
"compat": {
|
|
106
|
+
"artifactSchema": 3,
|
|
107
|
+
"runtimeAbi": 1
|
|
108
|
+
},
|
|
109
|
+
"entryEvent": {
|
|
110
|
+
"type": "START_BRANCH",
|
|
111
|
+
"textField": "callerInput",
|
|
112
|
+
"contextField": "callerInput"
|
|
162
113
|
},
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
114
|
+
"bossEvents": [],
|
|
115
|
+
"roleStates": {
|
|
116
|
+
"createBranch": {
|
|
117
|
+
"role": "coder",
|
|
118
|
+
"label": "Coder is identifying the request's issue and creating and checking out a new branch at the current commit."
|
|
119
|
+
}
|
|
169
120
|
},
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
fields: {
|
|
179
|
-
branch: 'semantic',
|
|
180
|
-
// DR-045: the unchanged receipt observes the commit the branch
|
|
181
|
-
// was created from as HEAD; the reconciler injects it here.
|
|
182
|
-
baseRevision: 'effect',
|
|
183
|
-
issueSummary: 'semantic',
|
|
121
|
+
"outcomeAuthority": {
|
|
122
|
+
"governedPlayerStates": {
|
|
123
|
+
"createBranch": {
|
|
124
|
+
"branched": {
|
|
125
|
+
"fields": {
|
|
126
|
+
"branch": "semantic",
|
|
127
|
+
"baseRevision": "effect",
|
|
128
|
+
"issueSummary": "semantic"
|
|
184
129
|
},
|
|
185
|
-
repositoryDisposition:
|
|
130
|
+
"repositoryDisposition": "unchanged"
|
|
186
131
|
},
|
|
187
|
-
refused: {
|
|
188
|
-
fields: {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
fields: { question: 'presentation' },
|
|
193
|
-
repositoryDisposition: 'unchanged',
|
|
132
|
+
"refused": {
|
|
133
|
+
"fields": {
|
|
134
|
+
"coderOutput": "presentation"
|
|
135
|
+
},
|
|
136
|
+
"repositoryDisposition": "unchanged"
|
|
194
137
|
},
|
|
195
|
-
|
|
196
|
-
|
|
138
|
+
"needsBossReply": {
|
|
139
|
+
"fields": {
|
|
140
|
+
"question": "presentation"
|
|
141
|
+
},
|
|
142
|
+
"repositoryDisposition": "unchanged"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
197
146
|
},
|
|
198
|
-
|
|
199
|
-
|
|
147
|
+
"placeholderFields": {},
|
|
148
|
+
"transitionEventFields": [
|
|
149
|
+
"callerInput",
|
|
150
|
+
"answer",
|
|
151
|
+
"questionId"
|
|
152
|
+
],
|
|
153
|
+
"controlContextFields": []
|
|
154
|
+
},
|
|
155
|
+
snapshotOptions: validateOptions,
|
|
156
|
+
machineInput: (options: PlaybookRuntimeOptions) => Object.fromEntries(
|
|
157
|
+
Object.entries(INPUT_MAPPING).filter(([, key]) => options[key as keyof PlaybookRuntimeOptions] !== undefined)
|
|
158
|
+
.map(([field, key]) => [field, options[key as keyof PlaybookRuntimeOptions]]),
|
|
159
|
+
),
|
|
200
160
|
verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
|
|
201
|
-
|
|
161
|
+
resumableStateIds: RESUMABLE_STATE_IDS,
|
|
202
162
|
unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
|
|
203
|
-
|
|
204
|
-
} satisfies XStatePlaybookRuntimeSpecV3<BranchPlaybookOptions>;
|
|
163
|
+
} satisfies XStatePlaybookRuntimeSpecV3<PlaybookRuntimeOptions>;
|
|
205
164
|
|
|
165
|
+
export const _internal = {
|
|
166
|
+
composePlayerPrompt,
|
|
167
|
+
RESUMABLE_STATE_IDS,
|
|
168
|
+
UNFINISHED_FINAL_STATE_IDS,
|
|
169
|
+
VERBATIM_PAYLOAD_FIELDS,
|
|
170
|
+
};
|
|
206
171
|
const createPlaybookRuntime: XStatePlaybookRuntimeFactory<
|
|
207
|
-
XStatePlaybookRuntimeConstruction<
|
|
208
|
-
|
|
209
|
-
BranchPlaybookHostCapabilities
|
|
210
|
-
>,
|
|
211
|
-
3
|
|
212
|
-
> = createXStatePlaybookRuntime<
|
|
213
|
-
BranchPlaybookOptions,
|
|
214
|
-
BranchPlaybookHostCapabilities
|
|
215
|
-
>(branchMachine, runtimeSpec);
|
|
216
|
-
|
|
172
|
+
XStatePlaybookRuntimeConstruction<PlaybookRuntimeOptions, PlaybookHostCapabilities>, 3
|
|
173
|
+
> = createXStatePlaybookRuntime<PlaybookRuntimeOptions, PlaybookHostCapabilities>(machine, runtimeSpec);
|
|
217
174
|
export default createPlaybookRuntime;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type PlaybookHostCapabilities, type PlaybookRuntime } from './branch.playbook.js';
|
|
2
|
+
import type { PlaybookHostConstructionCapabilities } from '../code.playbook/playbook-captain.js';
|
|
2
3
|
export interface PlaybookSummaryPolicy {
|
|
3
4
|
stateCountLabels: Readonly<Record<string, string>>;
|
|
4
5
|
copyPasteGuardNames: readonly string[];
|
|
@@ -24,7 +25,7 @@ export interface BranchPlaybookRegistryEntry {
|
|
|
24
25
|
concurrentRoleSets: readonly [];
|
|
25
26
|
summaryPolicy: PlaybookSummaryPolicy;
|
|
26
27
|
validateOptions(optionSlice: unknown): BranchOptions;
|
|
27
|
-
createRuntime(options: BranchOptions, hostCapabilities:
|
|
28
|
+
createRuntime(options: BranchOptions, hostCapabilities: PlaybookHostConstructionCapabilities & PlaybookHostCapabilities): PlaybookRuntime;
|
|
28
29
|
}
|
|
29
30
|
export declare const branchStateCountLabels: {
|
|
30
31
|
readonly createBranch: "branching round";
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
3
3
|
|
|
4
4
|
import createPlaybookRuntime, {
|
|
5
|
-
type
|
|
5
|
+
type PlaybookHostCapabilities,
|
|
6
6
|
type PlaybookRuntime,
|
|
7
7
|
} from './branch.playbook.js';
|
|
8
|
+
import type { PlaybookHostConstructionCapabilities } from '../code.playbook/playbook-captain.js';
|
|
8
9
|
|
|
9
10
|
export interface PlaybookSummaryPolicy {
|
|
10
11
|
stateCountLabels: Readonly<Record<string, string>>;
|
|
@@ -33,9 +34,12 @@ export interface BranchPlaybookRegistryEntry {
|
|
|
33
34
|
concurrentRoleSets: readonly [];
|
|
34
35
|
summaryPolicy: PlaybookSummaryPolicy;
|
|
35
36
|
validateOptions(optionSlice: unknown): BranchOptions;
|
|
37
|
+
// The linked module types live authority as opaque (link-materialization);
|
|
38
|
+
// the Captain entry binds its own construction capabilities here.
|
|
36
39
|
createRuntime(
|
|
37
40
|
options: BranchOptions,
|
|
38
|
-
hostCapabilities:
|
|
41
|
+
hostCapabilities: PlaybookHostConstructionCapabilities &
|
|
42
|
+
PlaybookHostCapabilities,
|
|
39
43
|
): PlaybookRuntime;
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -520,8 +520,6 @@ function projectionText(projection) {
|
|
|
520
520
|
return JSON.stringify(projection);
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
-
const MISSING_CONTENT_IDENTITY = Object.freeze({ kind: 'missing' });
|
|
524
|
-
|
|
525
523
|
function hasPath(projection, path) {
|
|
526
524
|
return Object.prototype.hasOwnProperty.call(projection, path);
|
|
527
525
|
}
|
|
@@ -555,9 +553,9 @@ function sameTreeEntry(left, right) {
|
|
|
555
553
|
return left.mode === right.mode && left.oid === right.oid;
|
|
556
554
|
}
|
|
557
555
|
|
|
558
|
-
// The
|
|
556
|
+
// The entry records nothing at the path: a deleted worktree entry, or a
|
|
559
557
|
// staged deletion whose index holds no object.
|
|
560
|
-
function
|
|
558
|
+
function entryIsDeletion(entry) {
|
|
561
559
|
return entry.worktree === undefined
|
|
562
560
|
? entry.indexMode === '000000'
|
|
563
561
|
: entry.worktree.kind === 'missing';
|
|
@@ -615,14 +613,57 @@ function sameContentIdentity(left, right) {
|
|
|
615
613
|
);
|
|
616
614
|
}
|
|
617
615
|
|
|
616
|
+
// A worktree deletion loses the Boss's content only when neither the after
|
|
617
|
+
// index nor the after HEAD tree holds it; content the after HEAD cannot
|
|
618
|
+
// produce fails closed as lost.
|
|
619
|
+
async function deletionRetainsContent(baseline, after, path, entry, afterEntry) {
|
|
620
|
+
try {
|
|
621
|
+
const baselineIdentity = await baselineContentIdentity(after.worktree, entry);
|
|
622
|
+
if (
|
|
623
|
+
afterEntry.kind === 'ordinary' &&
|
|
624
|
+
afterEntry.indexMode !== undefined &&
|
|
625
|
+
afterEntry.indexMode !== '000000'
|
|
626
|
+
) {
|
|
627
|
+
const indexIdentity = await blobContentIdentity(
|
|
628
|
+
after.worktree,
|
|
629
|
+
afterEntry.indexMode,
|
|
630
|
+
afterEntry.indexOid,
|
|
631
|
+
);
|
|
632
|
+
if (sameContentIdentity(baselineIdentity, indexIdentity)) return true;
|
|
633
|
+
}
|
|
634
|
+
if (baseline.head === after.head) return false;
|
|
635
|
+
const treeEntry = await afterTreeEntry(after.worktree, after.head, path);
|
|
636
|
+
if (treeEntry === undefined || treeEntry.mode === '160000') return false;
|
|
637
|
+
const treeIdentity = await blobContentIdentity(
|
|
638
|
+
after.worktree,
|
|
639
|
+
treeEntry.mode,
|
|
640
|
+
treeEntry.oid,
|
|
641
|
+
);
|
|
642
|
+
return sameContentIdentity(baselineIdentity, treeIdentity);
|
|
643
|
+
} catch {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
618
648
|
// DR-062 §1: the fate of one baseline projection entry at the end of a
|
|
619
649
|
// governed call — `preserved`, `altered-uncommitted`, `altered-committed`,
|
|
620
|
-
// `absorbed`, or `lost`.
|
|
650
|
+
// `absorbed`, or `lost`. Loss is decided before alteration: a kind that
|
|
651
|
+
// admits no content matching, a worktree deletion whose content survives in
|
|
652
|
+
// neither the index nor the new HEAD, and a commit that dropped the path all
|
|
653
|
+
// leave the Boss's content nowhere.
|
|
621
654
|
async function baselineEntryFate(baseline, after, path, entry) {
|
|
622
655
|
if (hasPath(after.projection, path)) {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
656
|
+
const afterEntry = after.projection[path];
|
|
657
|
+
if (JSON.stringify(afterEntry) === JSON.stringify(entry)) {
|
|
658
|
+
return 'preserved';
|
|
659
|
+
}
|
|
660
|
+
if (nonComparableBaselineEntry(entry)) return 'lost';
|
|
661
|
+
if (entryIsDeletion(afterEntry) && !entryIsDeletion(entry)) {
|
|
662
|
+
return (await deletionRetainsContent(baseline, after, path, entry, afterEntry))
|
|
663
|
+
? 'altered-uncommitted'
|
|
664
|
+
: 'lost';
|
|
665
|
+
}
|
|
666
|
+
return 'altered-uncommitted';
|
|
626
667
|
}
|
|
627
668
|
// The entry left the working tree, so only a commit this call made can hold
|
|
628
669
|
// the Boss's content; a call that moved no HEAD made none.
|
|
@@ -635,15 +676,15 @@ async function baselineEntryFate(baseline, after, path, entry) {
|
|
|
635
676
|
// carried no change of the Boss's and his content is nowhere.
|
|
636
677
|
return 'lost';
|
|
637
678
|
}
|
|
638
|
-
if (
|
|
679
|
+
if (entryIsDeletion(entry)) {
|
|
639
680
|
return treeEntry === undefined ? 'absorbed' : 'lost';
|
|
640
681
|
}
|
|
641
|
-
|
|
682
|
+
// A commit that dropped the path, or replaced it with a gitlink, carries
|
|
683
|
+
// no content of the Boss's to compare.
|
|
684
|
+
if (treeEntry === undefined || treeEntry.mode === '160000') return 'lost';
|
|
642
685
|
const [baselineIdentity, treeIdentity] = await Promise.all([
|
|
643
686
|
baselineContentIdentity(after.worktree, entry),
|
|
644
|
-
treeEntry
|
|
645
|
-
? MISSING_CONTENT_IDENTITY
|
|
646
|
-
: blobContentIdentity(after.worktree, treeEntry.mode, treeEntry.oid),
|
|
687
|
+
blobContentIdentity(after.worktree, treeEntry.mode, treeEntry.oid),
|
|
647
688
|
]);
|
|
648
689
|
return sameContentIdentity(baselineIdentity, treeIdentity)
|
|
649
690
|
? 'absorbed'
|