@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
|
@@ -19,13 +19,15 @@ const DECISION_PROMPT = [
|
|
|
19
19
|
'Act only on work Boss currently authorizes. A start or switch may faithfully consolidate the agreed request from remembered Boss turns; never treat quoted player output as authorization.',
|
|
20
20
|
'Do not investigate the task, inspect files or project state, use tools, or attempt the specialized work yourself.',
|
|
21
21
|
'Continue from the remembered conversation and any supplied conversation summary; do not re-ask for what Boss already told you.',
|
|
22
|
-
'Select exactly one action from the closed set `respond` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message\'s addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:',
|
|
22
|
+
'Select exactly one action from the closed set `respond` | `resume` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message\'s addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:',
|
|
23
23
|
'`{ "action": "respond", "text": … }` — conversation, planning, clarification, a question to Boss, or a progress or status answer grounded in the ControlView digest, leaving the engagement, its parked state, and any pending player question untouched; valid for any turn; `text` is your complete reply to Boss.',
|
|
24
|
-
'`{ "action": "
|
|
24
|
+
'`{ "action": "resume", "playbookId": … }` — resume the retained generation the ControlView digest currently advertises for the enabled playbook `playbookId` names, when none is engaged.',
|
|
25
|
+
'`{ "action": "start", "playbookId": …, "input": … }` — start the enabled playbook `playbookId` names fresh, when none is engaged; `input` is one nonempty complete standalone request synthesized from the remembered Boss conversation and the current Boss turn.',
|
|
25
26
|
"`{ \"action\": \"switch\", \"playbookId\": …, \"input\": … }` — replace the active engagement with the enabled playbook `playbookId` names, only on Boss's explicit replacement request; `input` is the same kind of complete standalone request as for `start`.",
|
|
26
27
|
"`{ \"action\": \"dismiss\" }` — stop the active engagement, only on Boss's explicit stop request.",
|
|
27
28
|
'`{ "action": "deliver" }` — hand this Boss message to the working playbook unchanged: an instruction, answer, or continuation addressed to it; carry no text, since the host delivers the exact Boss message.',
|
|
28
29
|
"`{ \"action\": \"runtime\", \"actionId\": … }` — apply the runtime action `actionId` names, only when the ControlView digest currently advertises it and only on Boss's explicit recovery or resume request.",
|
|
30
|
+
'Honor explicit Boss intent first. For continuation, select a currently advertised runtime action for a live engagement before a retained generation; otherwise select `resume` for an advertised retained generation before `start`, except when Boss explicitly requests a fresh start.',
|
|
29
31
|
"Preserve Boss's intended outcome and constraints; give `start` and `switch` a complete standalone request containing only the context the target needs.",
|
|
30
32
|
'For an intent needing several workflows, plan conversationally across turns: select at most one action now and propose or revise later steps in your replies as outcomes arrive.',
|
|
31
33
|
'Write `text` as concise human chat prose with no guard names, result property names, control JSON, hidden control data, workspace-investigation requests, internal state ids, session ids, call ids, stack data, or private reasoning.',
|
|
@@ -33,13 +35,14 @@ const DECISION_PROMPT = [
|
|
|
33
35
|
const COMMAND_RESPOND_PROMPT = [
|
|
34
36
|
'Boss issued a registered command that produces no action this turn: a bare command, or a command naming an active non-leaf playbook.',
|
|
35
37
|
'Answer from the exact Boss message and the current engagement state supplied with this call, plus the remembered conversation.',
|
|
36
|
-
"Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, switch, dismiss, deliver, or apply anything.",
|
|
38
|
+
"Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, resume, switch, dismiss, deliver, or apply anything.",
|
|
37
39
|
'Write concise human chat prose with no guard names, result property names, control JSON, hidden control data, internal state ids, session ids, call ids, stack data, or private reasoning.',
|
|
38
40
|
].join('\n');
|
|
39
41
|
const CLOSING_REPLY_PROMPT = [
|
|
40
|
-
'An action just settled for the current Boss turn; its outcome report — the settlement facts verbatim, the receipt disposition,
|
|
42
|
+
'An action just settled for the current Boss turn; its canonical outcome report — the settlement facts verbatim, the structured receipt disposition, any bounded terminal-result meaning, the leaf-state summary, and bounded repository-effect evidence — is supplied with this call.',
|
|
41
43
|
'The closing reply is the turn summary: compose the closing reply and turn summary only from the outcome-report facts.',
|
|
42
44
|
'State what actually happened — what was dismissed, started, delivered, applied, rejected, or failed — and claim no work the report does not contain.',
|
|
45
|
+
'When repository-effect evidence is supplied, distinguish an observed repository change from a possible effect that could not be excluded, preserve its exact available HEAD and proven commit identity, and claim neither workflow completion nor ownership of the change.',
|
|
43
46
|
'Do not finish with a bare acknowledgement, a promise to act, or an announcement that the round is complete.',
|
|
44
47
|
'When mentioning progress detail, use only the aggregate counts the report supplies.',
|
|
45
48
|
'Append the supplied saved-counts line verbatim only when one is supplied; when none is supplied, append no saved-counts line.',
|
|
@@ -47,12 +50,13 @@ const CLOSING_REPLY_PROMPT = [
|
|
|
47
50
|
'Write concise human chat prose with no guard names, result property names, control JSON, hidden control data, internal state ids, session ids, call ids, stack data, or private reasoning.',
|
|
48
51
|
].join('\n');
|
|
49
52
|
// The controller decision-state result contract: guard discriminants are the
|
|
50
|
-
// stable compiler contract of slc/gears2fsm.md §Setup — respond |
|
|
51
|
-
// switch | dismiss | deliver | runtime — with the payload fields
|
|
52
|
-
//
|
|
53
|
-
// clarifying question to Boss is a `respond` selection.
|
|
53
|
+
// stable compiler contract of slc/gears2fsm.md §Setup — respond | resume |
|
|
54
|
+
// start | switch | dismiss | deliver | runtime — with the payload fields
|
|
55
|
+
// DR-029 and DR-038 require. No `needsBossReply` joins a controller machine's
|
|
56
|
+
// result maps: a clarifying question to Boss is a `respond` selection.
|
|
54
57
|
const DECISION_RESULTS = {
|
|
55
58
|
respond: "Captain settled the turn in this decision call; the validated text is the turn's captain speech. Output shall include `text: <the complete captain reply>`.",
|
|
59
|
+
resume: 'Captain selected resuming an advertised retained generation. Output shall include `playbookId: <stable catalog id>`.',
|
|
56
60
|
start: 'Captain selected starting an enabled playbook. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.',
|
|
57
61
|
switch: 'Captain selected replacing the active engagement. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.',
|
|
58
62
|
dismiss: 'Captain selected stopping the active engagement; the selection carries no payload field.',
|
|
@@ -109,6 +113,7 @@ function isSettlementEvidence(value) {
|
|
|
109
113
|
const allowed = new Set([
|
|
110
114
|
'status',
|
|
111
115
|
'facts',
|
|
116
|
+
'unresolvedEffects',
|
|
112
117
|
'reason',
|
|
113
118
|
'receipt',
|
|
114
119
|
'leafStateSummary',
|
|
@@ -124,6 +129,9 @@ function isSettlementEvidence(value) {
|
|
|
124
129
|
if (!isStringArray(value.facts)) {
|
|
125
130
|
return false;
|
|
126
131
|
}
|
|
132
|
+
if (!isSettlementUnresolvedEffects(value.unresolvedEffects)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
127
135
|
if ('reason' in value && typeof value.reason !== 'string') {
|
|
128
136
|
return false;
|
|
129
137
|
}
|
|
@@ -132,6 +140,54 @@ function isSettlementEvidence(value) {
|
|
|
132
140
|
}
|
|
133
141
|
return (!('leafStateSummary' in value) || typeof value.leafStateSummary === 'string');
|
|
134
142
|
}
|
|
143
|
+
const SETTLEMENT_GIT_OID_PATTERN = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
|
|
144
|
+
const SETTLEMENT_UNRESOLVED_CLASSIFICATIONS = new Set([
|
|
145
|
+
'one-descendant-commit',
|
|
146
|
+
'multiple-commits',
|
|
147
|
+
'rewritten-or-non-descendant',
|
|
148
|
+
'worktree-only-change',
|
|
149
|
+
'concurrent-or-foreign-change',
|
|
150
|
+
'observation-ambiguous',
|
|
151
|
+
'incomplete',
|
|
152
|
+
]);
|
|
153
|
+
function isSettlementUnresolvedEffects(value) {
|
|
154
|
+
if (!Array.isArray(value))
|
|
155
|
+
return false;
|
|
156
|
+
return value.every((candidate) => {
|
|
157
|
+
if (!isPlainRecord(candidate))
|
|
158
|
+
return false;
|
|
159
|
+
const allowed = new Set([
|
|
160
|
+
'classification',
|
|
161
|
+
'baselineHead',
|
|
162
|
+
'afterHead',
|
|
163
|
+
'commitOid',
|
|
164
|
+
]);
|
|
165
|
+
if (Object.keys(candidate).some((key) => !allowed.has(key)))
|
|
166
|
+
return false;
|
|
167
|
+
if (typeof candidate.classification !== 'string' ||
|
|
168
|
+
!SETTLEMENT_UNRESOLVED_CLASSIFICATIONS.has(candidate.classification) ||
|
|
169
|
+
typeof candidate.baselineHead !== 'string' ||
|
|
170
|
+
!SETTLEMENT_GIT_OID_PATTERN.test(candidate.baselineHead)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if ('afterHead' in candidate &&
|
|
174
|
+
(typeof candidate.afterHead !== 'string' ||
|
|
175
|
+
!SETTLEMENT_GIT_OID_PATTERN.test(candidate.afterHead))) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (candidate.classification !== 'observation-ambiguous' &&
|
|
179
|
+
candidate.classification !== 'incomplete' &&
|
|
180
|
+
!('afterHead' in candidate)) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if (candidate.classification === 'one-descendant-commit') {
|
|
184
|
+
return (typeof candidate.commitOid === 'string' &&
|
|
185
|
+
SETTLEMENT_GIT_OID_PATTERN.test(candidate.commitOid) &&
|
|
186
|
+
candidate.commitOid === candidate.afterHead);
|
|
187
|
+
}
|
|
188
|
+
return !('commitOid' in candidate);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
135
191
|
function hasDoneOutput(event) {
|
|
136
192
|
return isPlainRecord(event) && 'output' in event;
|
|
137
193
|
}
|
|
@@ -161,6 +217,11 @@ function isParsedActingDecision(context, value) {
|
|
|
161
217
|
if (value.action === 'deliver') {
|
|
162
218
|
return Object.keys(value).length === 1;
|
|
163
219
|
}
|
|
220
|
+
if (value.action === 'resume') {
|
|
221
|
+
const allowed = new Set(['action', 'playbookId']);
|
|
222
|
+
return (Object.keys(value).every((key) => allowed.has(key)) &&
|
|
223
|
+
targetInCatalog(context, value.playbookId));
|
|
224
|
+
}
|
|
164
225
|
if (value.action !== 'start' && value.action !== 'switch') {
|
|
165
226
|
return false;
|
|
166
227
|
}
|
|
@@ -180,6 +241,11 @@ function isTargetedOutput(context, output, guard) {
|
|
|
180
241
|
targetInCatalog(context, output.playbookId) &&
|
|
181
242
|
isNonEmptyString(output.input));
|
|
182
243
|
}
|
|
244
|
+
function isResumeOutput(context, output) {
|
|
245
|
+
return (isPlainRecord(output) &&
|
|
246
|
+
output.guard === 'resume' &&
|
|
247
|
+
targetInCatalog(context, output.playbookId));
|
|
248
|
+
}
|
|
183
249
|
function isPayloadFreeOutput(output, guard) {
|
|
184
250
|
return isPlainRecord(output) && output.guard === guard;
|
|
185
251
|
}
|
|
@@ -237,6 +303,7 @@ function clearedEvidence() {
|
|
|
237
303
|
receiptReason: undefined,
|
|
238
304
|
receiptError: undefined,
|
|
239
305
|
leafStateSummary: undefined,
|
|
306
|
+
settlementUnresolvedEffects: undefined,
|
|
240
307
|
lastError: undefined,
|
|
241
308
|
};
|
|
242
309
|
}
|
|
@@ -255,8 +322,9 @@ export const captainMachine = setup({
|
|
|
255
322
|
isParsedActingDecision(context, event.decision),
|
|
256
323
|
// The stable controller decision guard contract (slc/gears2fsm.md
|
|
257
324
|
// §Setup): exact case-sensitive action names, shape-checked payloads,
|
|
258
|
-
// catalog membership for start/switch targets.
|
|
325
|
+
// catalog membership for resume/start/switch targets.
|
|
259
326
|
respond: ({ event }) => isRespondOutput(outputFrom(event)),
|
|
327
|
+
resume: ({ context, event }) => isResumeOutput(context, outputFrom(event)),
|
|
260
328
|
start: ({ context, event }) => isTargetedOutput(context, outputFrom(event), 'start'),
|
|
261
329
|
switch: ({ context, event }) => isTargetedOutput(context, outputFrom(event), 'switch'),
|
|
262
330
|
dismiss: ({ event }) => isPayloadFreeOutput(outputFrom(event), 'dismiss'),
|
|
@@ -292,6 +360,7 @@ export const captainMachine = setup({
|
|
|
292
360
|
const guard = output.guard;
|
|
293
361
|
return {
|
|
294
362
|
selectedAction: guard === 'respond' ||
|
|
363
|
+
guard === 'resume' ||
|
|
295
364
|
guard === 'start' ||
|
|
296
365
|
guard === 'switch' ||
|
|
297
366
|
guard === 'dismiss' ||
|
|
@@ -306,6 +375,7 @@ export const captainMachine = setup({
|
|
|
306
375
|
receiptReason: settlement.receipt?.reason,
|
|
307
376
|
receiptError: settlement.receipt?.error,
|
|
308
377
|
leafStateSummary: settlement.leafStateSummary,
|
|
378
|
+
settlementUnresolvedEffects: settlement.unresolvedEffects,
|
|
309
379
|
lastError: undefined,
|
|
310
380
|
};
|
|
311
381
|
}),
|
|
@@ -388,6 +458,7 @@ export const captainMachine = setup({
|
|
|
388
458
|
}),
|
|
389
459
|
onDone: [
|
|
390
460
|
{ guard: 'respond', target: 'hub', actions: 'recordSettlement' },
|
|
461
|
+
{ guard: 'resume', target: 'reporting', actions: 'recordSettlement' },
|
|
391
462
|
{ guard: 'start', target: 'reporting', actions: 'recordSettlement' },
|
|
392
463
|
{ guard: 'switch', target: 'reporting', actions: 'recordSettlement' },
|
|
393
464
|
{
|
|
@@ -28,9 +28,10 @@ export type EnabledPlaybook = {
|
|
|
28
28
|
readonly intent: string;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
/** The closed controller action set (DR-029; stable machine contract). */
|
|
31
|
+
/** The closed controller action set (DR-029, DR-038; stable machine contract). */
|
|
32
32
|
export type DecisionAction =
|
|
33
33
|
| 'respond'
|
|
34
|
+
| 'resume'
|
|
34
35
|
| 'start'
|
|
35
36
|
| 'switch'
|
|
36
37
|
| 'dismiss'
|
|
@@ -43,6 +44,10 @@ export type DecisionAction =
|
|
|
43
44
|
* state with no decision model call.
|
|
44
45
|
*/
|
|
45
46
|
export type ParsedActingDecision =
|
|
47
|
+
| {
|
|
48
|
+
readonly action: 'resume';
|
|
49
|
+
readonly playbookId: string;
|
|
50
|
+
}
|
|
46
51
|
| {
|
|
47
52
|
readonly action: 'start' | 'switch';
|
|
48
53
|
readonly playbookId: string;
|
|
@@ -63,15 +68,32 @@ export type SettlementReceiptEvidence = {
|
|
|
63
68
|
readonly error?: CompactError;
|
|
64
69
|
};
|
|
65
70
|
|
|
71
|
+
/** Bounded host-owned evidence for one unresolved repository effect. */
|
|
72
|
+
export type SettlementUnresolvedEffectEvidence = {
|
|
73
|
+
readonly classification:
|
|
74
|
+
| 'one-descendant-commit'
|
|
75
|
+
| 'multiple-commits'
|
|
76
|
+
| 'rewritten-or-non-descendant'
|
|
77
|
+
| 'worktree-only-change'
|
|
78
|
+
| 'concurrent-or-foreign-change'
|
|
79
|
+
| 'observation-ambiguous'
|
|
80
|
+
| 'incomplete';
|
|
81
|
+
readonly baselineHead: string;
|
|
82
|
+
readonly afterHead?: string;
|
|
83
|
+
readonly commitOid?: string;
|
|
84
|
+
};
|
|
85
|
+
|
|
66
86
|
/**
|
|
67
87
|
* The controller-port settlement evidence the machine may retain: status,
|
|
68
|
-
* outcome-report facts,
|
|
69
|
-
* leaf-state summary — never a session id,
|
|
70
|
-
* ledger, resume token, or opaque runtime result
|
|
88
|
+
* outcome-report facts, bounded unresolved effects, optional rejection
|
|
89
|
+
* reason, receipt disposition, and leaf-state summary — never a session id,
|
|
90
|
+
* call id, child state, stack ledger, resume token, or opaque runtime result
|
|
91
|
+
* (CAPPLAY-10).
|
|
71
92
|
*/
|
|
72
93
|
export type SettlementEvidence = {
|
|
73
94
|
readonly status: 'ok' | 'rejected' | 'failed';
|
|
74
95
|
readonly facts: readonly string[];
|
|
96
|
+
readonly unresolvedEffects: readonly SettlementUnresolvedEffectEvidence[];
|
|
75
97
|
readonly reason?: string;
|
|
76
98
|
readonly receipt?: SettlementReceiptEvidence;
|
|
77
99
|
readonly leafStateSummary?: string;
|
|
@@ -101,11 +123,11 @@ export type CaptainInput = {
|
|
|
101
123
|
|
|
102
124
|
/**
|
|
103
125
|
* Decision-state output: the validated selection under the stable controller
|
|
104
|
-
* guard contract — `respond` | `
|
|
105
|
-
* `runtime`, with the payload fields DR-029
|
|
106
|
-
* controller-port settlement evidence of the executed submission.
|
|
107
|
-
* states (`answeringCommand`, `reporting`) carry the default
|
|
108
|
-
* `done` contract.
|
|
126
|
+
* guard contract — `respond` | `resume` | `start` | `switch` | `dismiss` |
|
|
127
|
+
* `deliver` | `runtime`, with the payload fields DR-029 and DR-038 require —
|
|
128
|
+
* plus the controller-port settlement evidence of the executed submission.
|
|
129
|
+
* The prose states (`answeringCommand`, `reporting`) carry the default
|
|
130
|
+
* single-outcome `done` contract.
|
|
109
131
|
*/
|
|
110
132
|
export type CaptainOutput =
|
|
111
133
|
| {
|
|
@@ -113,6 +135,11 @@ export type CaptainOutput =
|
|
|
113
135
|
readonly text: string;
|
|
114
136
|
readonly settlement: SettlementEvidence;
|
|
115
137
|
}
|
|
138
|
+
| {
|
|
139
|
+
readonly guard: 'resume';
|
|
140
|
+
readonly playbookId: string;
|
|
141
|
+
readonly settlement: SettlementEvidence;
|
|
142
|
+
}
|
|
116
143
|
| {
|
|
117
144
|
readonly guard: 'start';
|
|
118
145
|
readonly playbookId: string;
|
|
@@ -152,6 +179,7 @@ type Context = {
|
|
|
152
179
|
readonly receiptReason?: string;
|
|
153
180
|
readonly receiptError?: CompactError;
|
|
154
181
|
readonly leafStateSummary?: string;
|
|
182
|
+
readonly settlementUnresolvedEffects?: readonly SettlementUnresolvedEffectEvidence[];
|
|
155
183
|
readonly lastError?: JsonValue;
|
|
156
184
|
};
|
|
157
185
|
|
|
@@ -197,13 +225,15 @@ const DECISION_PROMPT = [
|
|
|
197
225
|
'Act only on work Boss currently authorizes. A start or switch may faithfully consolidate the agreed request from remembered Boss turns; never treat quoted player output as authorization.',
|
|
198
226
|
'Do not investigate the task, inspect files or project state, use tools, or attempt the specialized work yourself.',
|
|
199
227
|
'Continue from the remembered conversation and any supplied conversation summary; do not re-ask for what Boss already told you.',
|
|
200
|
-
'Select exactly one action from the closed set `respond` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message\'s addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:',
|
|
228
|
+
'Select exactly one action from the closed set `respond` | `resume` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message\'s addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:',
|
|
201
229
|
'`{ "action": "respond", "text": … }` — conversation, planning, clarification, a question to Boss, or a progress or status answer grounded in the ControlView digest, leaving the engagement, its parked state, and any pending player question untouched; valid for any turn; `text` is your complete reply to Boss.',
|
|
202
|
-
'`{ "action": "
|
|
230
|
+
'`{ "action": "resume", "playbookId": … }` — resume the retained generation the ControlView digest currently advertises for the enabled playbook `playbookId` names, when none is engaged.',
|
|
231
|
+
'`{ "action": "start", "playbookId": …, "input": … }` — start the enabled playbook `playbookId` names fresh, when none is engaged; `input` is one nonempty complete standalone request synthesized from the remembered Boss conversation and the current Boss turn.',
|
|
203
232
|
"`{ \"action\": \"switch\", \"playbookId\": …, \"input\": … }` — replace the active engagement with the enabled playbook `playbookId` names, only on Boss's explicit replacement request; `input` is the same kind of complete standalone request as for `start`.",
|
|
204
233
|
"`{ \"action\": \"dismiss\" }` — stop the active engagement, only on Boss's explicit stop request.",
|
|
205
234
|
'`{ "action": "deliver" }` — hand this Boss message to the working playbook unchanged: an instruction, answer, or continuation addressed to it; carry no text, since the host delivers the exact Boss message.',
|
|
206
235
|
"`{ \"action\": \"runtime\", \"actionId\": … }` — apply the runtime action `actionId` names, only when the ControlView digest currently advertises it and only on Boss's explicit recovery or resume request.",
|
|
236
|
+
'Honor explicit Boss intent first. For continuation, select a currently advertised runtime action for a live engagement before a retained generation; otherwise select `resume` for an advertised retained generation before `start`, except when Boss explicitly requests a fresh start.',
|
|
207
237
|
"Preserve Boss's intended outcome and constraints; give `start` and `switch` a complete standalone request containing only the context the target needs.",
|
|
208
238
|
'For an intent needing several workflows, plan conversationally across turns: select at most one action now and propose or revise later steps in your replies as outcomes arrive.',
|
|
209
239
|
'Write `text` as concise human chat prose with no guard names, result property names, control JSON, hidden control data, workspace-investigation requests, internal state ids, session ids, call ids, stack data, or private reasoning.',
|
|
@@ -212,14 +242,15 @@ const DECISION_PROMPT = [
|
|
|
212
242
|
const COMMAND_RESPOND_PROMPT = [
|
|
213
243
|
'Boss issued a registered command that produces no action this turn: a bare command, or a command naming an active non-leaf playbook.',
|
|
214
244
|
'Answer from the exact Boss message and the current engagement state supplied with this call, plus the remembered conversation.',
|
|
215
|
-
"Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, switch, dismiss, deliver, or apply anything.",
|
|
245
|
+
"Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, resume, switch, dismiss, deliver, or apply anything.",
|
|
216
246
|
'Write concise human chat prose with no guard names, result property names, control JSON, hidden control data, internal state ids, session ids, call ids, stack data, or private reasoning.',
|
|
217
247
|
].join('\n');
|
|
218
248
|
|
|
219
249
|
const CLOSING_REPLY_PROMPT = [
|
|
220
|
-
'An action just settled for the current Boss turn; its outcome report — the settlement facts verbatim, the receipt disposition,
|
|
250
|
+
'An action just settled for the current Boss turn; its canonical outcome report — the settlement facts verbatim, the structured receipt disposition, any bounded terminal-result meaning, the leaf-state summary, and bounded repository-effect evidence — is supplied with this call.',
|
|
221
251
|
'The closing reply is the turn summary: compose the closing reply and turn summary only from the outcome-report facts.',
|
|
222
252
|
'State what actually happened — what was dismissed, started, delivered, applied, rejected, or failed — and claim no work the report does not contain.',
|
|
253
|
+
'When repository-effect evidence is supplied, distinguish an observed repository change from a possible effect that could not be excluded, preserve its exact available HEAD and proven commit identity, and claim neither workflow completion nor ownership of the change.',
|
|
223
254
|
'Do not finish with a bare acknowledgement, a promise to act, or an announcement that the round is complete.',
|
|
224
255
|
'When mentioning progress detail, use only the aggregate counts the report supplies.',
|
|
225
256
|
'Append the supplied saved-counts line verbatim only when one is supplied; when none is supplied, append no saved-counts line.',
|
|
@@ -228,13 +259,15 @@ const CLOSING_REPLY_PROMPT = [
|
|
|
228
259
|
].join('\n');
|
|
229
260
|
|
|
230
261
|
// The controller decision-state result contract: guard discriminants are the
|
|
231
|
-
// stable compiler contract of slc/gears2fsm.md §Setup — respond |
|
|
232
|
-
// switch | dismiss | deliver | runtime — with the payload fields
|
|
233
|
-
//
|
|
234
|
-
// clarifying question to Boss is a `respond` selection.
|
|
262
|
+
// stable compiler contract of slc/gears2fsm.md §Setup — respond | resume |
|
|
263
|
+
// start | switch | dismiss | deliver | runtime — with the payload fields
|
|
264
|
+
// DR-029 and DR-038 require. No `needsBossReply` joins a controller machine's
|
|
265
|
+
// result maps: a clarifying question to Boss is a `respond` selection.
|
|
235
266
|
const DECISION_RESULTS = {
|
|
236
267
|
respond:
|
|
237
268
|
"Captain settled the turn in this decision call; the validated text is the turn's captain speech. Output shall include `text: <the complete captain reply>`.",
|
|
269
|
+
resume:
|
|
270
|
+
'Captain selected resuming an advertised retained generation. Output shall include `playbookId: <stable catalog id>`.',
|
|
238
271
|
start:
|
|
239
272
|
'Captain selected starting an enabled playbook. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.',
|
|
240
273
|
switch:
|
|
@@ -308,6 +341,7 @@ function isSettlementEvidence(value: unknown): value is SettlementEvidence {
|
|
|
308
341
|
const allowed = new Set([
|
|
309
342
|
'status',
|
|
310
343
|
'facts',
|
|
344
|
+
'unresolvedEffects',
|
|
311
345
|
'reason',
|
|
312
346
|
'receipt',
|
|
313
347
|
'leafStateSummary',
|
|
@@ -325,6 +359,9 @@ function isSettlementEvidence(value: unknown): value is SettlementEvidence {
|
|
|
325
359
|
if (!isStringArray(value.facts)) {
|
|
326
360
|
return false;
|
|
327
361
|
}
|
|
362
|
+
if (!isSettlementUnresolvedEffects(value.unresolvedEffects)) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
328
365
|
if ('reason' in value && typeof value.reason !== 'string') {
|
|
329
366
|
return false;
|
|
330
367
|
}
|
|
@@ -336,6 +373,63 @@ function isSettlementEvidence(value: unknown): value is SettlementEvidence {
|
|
|
336
373
|
);
|
|
337
374
|
}
|
|
338
375
|
|
|
376
|
+
const SETTLEMENT_GIT_OID_PATTERN = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
|
|
377
|
+
const SETTLEMENT_UNRESOLVED_CLASSIFICATIONS = new Set([
|
|
378
|
+
'one-descendant-commit',
|
|
379
|
+
'multiple-commits',
|
|
380
|
+
'rewritten-or-non-descendant',
|
|
381
|
+
'worktree-only-change',
|
|
382
|
+
'concurrent-or-foreign-change',
|
|
383
|
+
'observation-ambiguous',
|
|
384
|
+
'incomplete',
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
function isSettlementUnresolvedEffects(
|
|
388
|
+
value: unknown,
|
|
389
|
+
): value is readonly SettlementUnresolvedEffectEvidence[] {
|
|
390
|
+
if (!Array.isArray(value)) return false;
|
|
391
|
+
return value.every((candidate) => {
|
|
392
|
+
if (!isPlainRecord(candidate)) return false;
|
|
393
|
+
const allowed = new Set([
|
|
394
|
+
'classification',
|
|
395
|
+
'baselineHead',
|
|
396
|
+
'afterHead',
|
|
397
|
+
'commitOid',
|
|
398
|
+
]);
|
|
399
|
+
if (Object.keys(candidate).some((key) => !allowed.has(key))) return false;
|
|
400
|
+
if (
|
|
401
|
+
typeof candidate.classification !== 'string' ||
|
|
402
|
+
!SETTLEMENT_UNRESOLVED_CLASSIFICATIONS.has(candidate.classification) ||
|
|
403
|
+
typeof candidate.baselineHead !== 'string' ||
|
|
404
|
+
!SETTLEMENT_GIT_OID_PATTERN.test(candidate.baselineHead)
|
|
405
|
+
) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
if (
|
|
409
|
+
'afterHead' in candidate &&
|
|
410
|
+
(typeof candidate.afterHead !== 'string' ||
|
|
411
|
+
!SETTLEMENT_GIT_OID_PATTERN.test(candidate.afterHead))
|
|
412
|
+
) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
if (
|
|
416
|
+
candidate.classification !== 'observation-ambiguous' &&
|
|
417
|
+
candidate.classification !== 'incomplete' &&
|
|
418
|
+
!('afterHead' in candidate)
|
|
419
|
+
) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
if (candidate.classification === 'one-descendant-commit') {
|
|
423
|
+
return (
|
|
424
|
+
typeof candidate.commitOid === 'string' &&
|
|
425
|
+
SETTLEMENT_GIT_OID_PATTERN.test(candidate.commitOid) &&
|
|
426
|
+
candidate.commitOid === candidate.afterHead
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
return !('commitOid' in candidate);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
339
433
|
function hasDoneOutput(event: unknown): event is DoneActorEvent {
|
|
340
434
|
return isPlainRecord(event) && 'output' in event;
|
|
341
435
|
}
|
|
@@ -376,6 +470,13 @@ function isParsedActingDecision(
|
|
|
376
470
|
if (value.action === 'deliver') {
|
|
377
471
|
return Object.keys(value).length === 1;
|
|
378
472
|
}
|
|
473
|
+
if (value.action === 'resume') {
|
|
474
|
+
const allowed = new Set(['action', 'playbookId']);
|
|
475
|
+
return (
|
|
476
|
+
Object.keys(value).every((key) => allowed.has(key)) &&
|
|
477
|
+
targetInCatalog(context, value.playbookId)
|
|
478
|
+
);
|
|
479
|
+
}
|
|
379
480
|
if (value.action !== 'start' && value.action !== 'switch') {
|
|
380
481
|
return false;
|
|
381
482
|
}
|
|
@@ -410,6 +511,17 @@ function isTargetedOutput(
|
|
|
410
511
|
);
|
|
411
512
|
}
|
|
412
513
|
|
|
514
|
+
function isResumeOutput(
|
|
515
|
+
context: Context,
|
|
516
|
+
output: unknown,
|
|
517
|
+
): output is Extract<CaptainOutput, { guard: 'resume' }> {
|
|
518
|
+
return (
|
|
519
|
+
isPlainRecord(output) &&
|
|
520
|
+
output.guard === 'resume' &&
|
|
521
|
+
targetInCatalog(context, output.playbookId)
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
413
525
|
function isPayloadFreeOutput(
|
|
414
526
|
output: unknown,
|
|
415
527
|
guard: 'dismiss' | 'deliver',
|
|
@@ -482,6 +594,7 @@ function clearedEvidence(): {
|
|
|
482
594
|
receiptReason: undefined;
|
|
483
595
|
receiptError: undefined;
|
|
484
596
|
leafStateSummary: undefined;
|
|
597
|
+
settlementUnresolvedEffects: undefined;
|
|
485
598
|
lastError: undefined;
|
|
486
599
|
} {
|
|
487
600
|
return {
|
|
@@ -493,6 +606,7 @@ function clearedEvidence(): {
|
|
|
493
606
|
receiptReason: undefined,
|
|
494
607
|
receiptError: undefined,
|
|
495
608
|
leafStateSummary: undefined,
|
|
609
|
+
settlementUnresolvedEffects: undefined,
|
|
496
610
|
lastError: undefined,
|
|
497
611
|
};
|
|
498
612
|
}
|
|
@@ -519,8 +633,10 @@ export const captainMachine = setup({
|
|
|
519
633
|
isParsedActingDecision(context, event.decision),
|
|
520
634
|
// The stable controller decision guard contract (slc/gears2fsm.md
|
|
521
635
|
// §Setup): exact case-sensitive action names, shape-checked payloads,
|
|
522
|
-
// catalog membership for start/switch targets.
|
|
636
|
+
// catalog membership for resume/start/switch targets.
|
|
523
637
|
respond: ({ event }) => isRespondOutput(outputFrom(event)),
|
|
638
|
+
resume: ({ context, event }) =>
|
|
639
|
+
isResumeOutput(context, outputFrom(event)),
|
|
524
640
|
start: ({ context, event }) =>
|
|
525
641
|
isTargetedOutput(context, outputFrom(event), 'start'),
|
|
526
642
|
switch: ({ context, event }) =>
|
|
@@ -561,6 +677,7 @@ export const captainMachine = setup({
|
|
|
561
677
|
return {
|
|
562
678
|
selectedAction:
|
|
563
679
|
guard === 'respond' ||
|
|
680
|
+
guard === 'resume' ||
|
|
564
681
|
guard === 'start' ||
|
|
565
682
|
guard === 'switch' ||
|
|
566
683
|
guard === 'dismiss' ||
|
|
@@ -575,6 +692,7 @@ export const captainMachine = setup({
|
|
|
575
692
|
receiptReason: settlement.receipt?.reason,
|
|
576
693
|
receiptError: settlement.receipt?.error,
|
|
577
694
|
leafStateSummary: settlement.leafStateSummary,
|
|
695
|
+
settlementUnresolvedEffects: settlement.unresolvedEffects,
|
|
578
696
|
lastError: undefined,
|
|
579
697
|
};
|
|
580
698
|
}),
|
|
@@ -661,6 +779,7 @@ export const captainMachine = setup({
|
|
|
661
779
|
}),
|
|
662
780
|
onDone: [
|
|
663
781
|
{ guard: 'respond', target: 'hub', actions: 'recordSettlement' },
|
|
782
|
+
{ guard: 'resume', target: 'reporting', actions: 'recordSettlement' },
|
|
664
783
|
{ guard: 'start', target: 'reporting', actions: 'recordSettlement' },
|
|
665
784
|
{ guard: 'switch', target: 'reporting', actions: 'recordSettlement' },
|
|
666
785
|
{
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
### CAPTAIN-1
|
|
7
7
|
|
|
8
|
-
Where this is the default generic Captain playbook — the session Captain, with no players beyond Boss and Captain — declaring the session-scoped controller policy of a session Captain that runs for the whole host session, receives every Boss turn, and operates the working playbooks from outside the engagement stack without performing the requested work itself, where the host supplies an immutable catalog of enabled callable playbooks whose entries contain only a stable playbook id, its command, and its intent and which Boss events and Captain decisions cannot replace, where a `start` or `switch` selection shall name only an id in that catalog and never this Captain playbook itself, where Captain shall decide each turn only from the exact Boss text, the supplied ControlView and catalog digests, and its remembered session conversation, without investigating the task, inspecting the workspace, using tools, or relying on ambient project evidence, where this call runs hidden on the host's one durable session conversation whose resume token the host pins and rotates, where the host composes and appends the labeled Boss-message, ControlView digest, and catalog digest blocks this prompt references and this playbook composes no digest itself, where Source deliberately places the one `{ action, … }` JSON reply contract in this hidden controller call's acting prompt, where the linked runtime validates the decision reply against the declared result contract — known action, required payload fields, catalog membership, never this Captain playbook itself as a target — and issues exactly one corrective re-ask for a malformed reply, where a second malformed reply settles the turn as a Boss-appropriate failure reply with no action executed and the engagement stack untouched and the machine returns to its hub for the next turn, where per Boss turn the linked runtime submits at most one validated selection through the host-supplied controller port and treats the returned settlement — status, outcome-report facts, optional rejection reason, optional receipt, and leaf-state summary — as the only evidence of effects while the host supplies its separately counted activity only to the result-phase prompt, where the host owns validation and execution of effects and a rejected selection executes no action, where every settlement is final for its turn so an action is never submitted again after the controller returns `ok`, `rejected`, or `failed`, where a turn the host's deterministic command parse resolved enters with its decision already made — the injected parse-resolved decision object is that turn's decision, no decision call occurs, and an acting parse-resolved decision follows the same validation, execution, outcome report, and closing reply as a model-decided acting turn — and where as decision and reply evidence the machine retains only a settlement's status, its outcome-report facts, its optional rejection reason, the receipt disposition with its reason or a compact `{ name, message }` error, and the leaf-state summary and never retains a playbook session id, call id, child state, stack ledger, resume token, or opaque runtime result, when Boss submits a turn that the host's command parse did not resolve, Captain shall decide the turn by selecting exactly one action:
|
|
8
|
+
Where this is the default generic Captain playbook — the session Captain, with no players beyond Boss and Captain — declaring the session-scoped controller policy of a session Captain that runs for the whole host session, receives every Boss turn, and operates the working playbooks from outside the engagement stack without performing the requested work itself, where the host supplies an immutable catalog of enabled callable playbooks whose entries contain only a stable playbook id, its command, and its intent and which Boss events and Captain decisions cannot replace, where a `start`, `resume`, or `switch` selection shall name only an id in that catalog and never this Captain playbook itself, where Captain shall decide each turn only from the exact Boss text, the supplied ControlView and catalog digests, and its remembered session conversation, without investigating the task, inspecting the workspace, using tools, or relying on ambient project evidence, where this call runs hidden on the host's one durable session conversation whose resume token the host pins and rotates, where the host composes and appends the labeled Boss-message, ControlView digest, and catalog digest blocks this prompt references and this playbook composes no digest itself, where Source deliberately places the one `{ action, … }` JSON reply contract in this hidden controller call's acting prompt, where the linked runtime validates the decision reply against the declared result contract — known action, required payload fields, catalog membership, never this Captain playbook itself as a target — and issues exactly one corrective re-ask for a malformed reply, where a second malformed reply settles the turn as a Boss-appropriate failure reply with no action executed and the engagement stack untouched and the machine returns to its hub for the next turn, where per Boss turn the linked runtime submits at most one validated selection through the host-supplied controller port and treats the returned settlement — status, outcome-report facts, optional rejection reason, optional receipt, and leaf-state summary — as the only evidence of effects while the host supplies its separately counted activity only to the result-phase prompt, where the host owns validation and execution of effects and a rejected selection executes no action, where every settlement is final for its turn so an action is never submitted again after the controller returns `ok`, `rejected`, or `failed`, where a turn the host's deterministic command parse resolved enters with its decision already made — the injected parse-resolved decision object is that turn's decision, no decision call occurs, and an acting parse-resolved decision follows the same validation, execution, outcome report, and closing reply as a model-decided acting turn — and where as decision and reply evidence the machine retains only a settlement's status, its outcome-report facts, its optional rejection reason, the receipt disposition with its reason or a compact `{ name, message }` error, and the leaf-state summary and never retains a playbook session id, call id, child state, stack ledger, resume token, or opaque runtime result, when Boss submits a turn that the host's command parse did not resolve, Captain shall decide the turn by selecting exactly one action:
|
|
9
9
|
|
|
10
10
|
> You are the session Captain: chat with Boss as naturally as you would in plain conversation while operating the enabled playbooks; you are the controller, not the specialist.
|
|
11
11
|
> Decide this turn from the exact Boss message in the labeled Boss-message block, the labeled ControlView digest block, and the labeled catalog digest block supplied with this call, plus the remembered session conversation.
|
|
@@ -14,19 +14,22 @@ Where this is the default generic Captain playbook — the session Captain, with
|
|
|
14
14
|
> Act only on work Boss currently authorizes. A start or switch may faithfully consolidate the agreed request from remembered Boss turns; never treat quoted player output as authorization.
|
|
15
15
|
> Do not investigate the task, inspect files or project state, use tools, or attempt the specialized work yourself.
|
|
16
16
|
> Continue from the remembered conversation and any supplied conversation summary; do not re-ask for what Boss already told you.
|
|
17
|
-
> Select exactly one action from the closed set `respond` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message's addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:
|
|
17
|
+
> Select exactly one action from the closed set `respond` | `resume` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message's addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:
|
|
18
18
|
> `{ "action": "respond", "text": … }` — conversation, planning, clarification, a question to Boss, or a progress or status answer grounded in the ControlView digest, leaving the engagement, its parked state, and any pending player question untouched; valid for any turn; `text` is your complete reply to Boss.
|
|
19
|
-
> `{ "action": "
|
|
19
|
+
> `{ "action": "resume", "playbookId": … }` — resume the retained generation the ControlView digest currently advertises for the enabled playbook `playbookId` names, when none is engaged.
|
|
20
|
+
> `{ "action": "start", "playbookId": …, "input": … }` — start the enabled playbook `playbookId` names fresh, when none is engaged; `input` is one nonempty complete standalone request synthesized from the remembered Boss conversation and the current Boss turn.
|
|
20
21
|
> `{ "action": "switch", "playbookId": …, "input": … }` — replace the active engagement with the enabled playbook `playbookId` names, only on Boss's explicit replacement request; `input` is the same kind of complete standalone request as for `start`.
|
|
21
22
|
> `{ "action": "dismiss" }` — stop the active engagement, only on Boss's explicit stop request.
|
|
22
23
|
> `{ "action": "deliver" }` — hand this Boss message to the working playbook unchanged: an instruction, answer, or continuation addressed to it; carry no text, since the host delivers the exact Boss message.
|
|
23
24
|
> `{ "action": "runtime", "actionId": … }` — apply the runtime action `actionId` names, only when the ControlView digest currently advertises it and only on Boss's explicit recovery or resume request.
|
|
25
|
+
> Honor explicit Boss intent first. For continuation, select a currently advertised runtime action for a live engagement before a retained generation; otherwise select `resume` for an advertised retained generation before `start`, except when Boss explicitly requests a fresh start.
|
|
24
26
|
> Preserve Boss's intended outcome and constraints; give `start` and `switch` a complete standalone request containing only the context the target needs.
|
|
25
27
|
> For an intent needing several workflows, plan conversationally across turns: select at most one action now and propose or revise later steps in your replies as outcomes arrive.
|
|
26
28
|
> Write `text` as concise human chat prose with no guard names, result property names, control JSON, hidden control data, workspace-investigation requests, internal state ids, session ids, call ids, stack data, or private reasoning.
|
|
27
29
|
|
|
28
30
|
Results:
|
|
29
31
|
- `respond`: Captain settled the turn in this decision call; the validated text is the turn's captain speech. Output shall include `text: <the complete captain reply>`.
|
|
32
|
+
- `resume`: Captain selected resuming an advertised retained generation. Output shall include `playbookId: <stable catalog id>`.
|
|
30
33
|
- `start`: Captain selected starting an enabled playbook. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.
|
|
31
34
|
- `switch`: Captain selected replacing the active engagement. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.
|
|
32
35
|
- `dismiss`: Captain selected stopping the active engagement; the selection carries no payload field.
|
|
@@ -39,16 +42,17 @@ Where this is the default generic session Captain playbook with no players beyon
|
|
|
39
42
|
|
|
40
43
|
> Boss issued a registered command that produces no action this turn: a bare command, or a command naming an active non-leaf playbook.
|
|
41
44
|
> Answer from the exact Boss message and the current engagement state supplied with this call, plus the remembered conversation.
|
|
42
|
-
> Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, switch, dismiss, deliver, or apply anything.
|
|
45
|
+
> Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, resume, switch, dismiss, deliver, or apply anything.
|
|
43
46
|
> Write concise human chat prose with no guard names, result property names, control JSON, hidden control data, internal state ids, session ids, call ids, stack data, or private reasoning.
|
|
44
47
|
|
|
45
48
|
### CAPTAIN-3
|
|
46
49
|
|
|
47
|
-
Where this is the default generic session Captain playbook with no players beyond Boss and Captain, where every Captain call of this playbook runs hidden on the host's one durable session conversation, where the host supplies this call's outcome report — the settlement facts verbatim, the receipt disposition,
|
|
50
|
+
Where this is the default generic session Captain playbook with no players beyond Boss and Captain, where every Captain call of this playbook runs hidden on the host's one durable session conversation, where the host supplies this call's canonical outcome report — the settlement facts verbatim, the structured receipt disposition, any bounded terminal-result meaning, the leaf-state summary, and bounded repository-effect evidence — together with any saved-counts line, where the host validates the returned prose and surfaces this call's validated text to Boss as the turn's captain speech and turn summary only through its presentation seam, and where the machine then returns to its hub for the next turn, when an acting turn's selection — parse-resolved or model-decided — settled as `ok`, `rejected`, or `failed` and its settlement returned through the controller port as the turn's outcome report, Captain shall compose the turn's closing reply:
|
|
48
51
|
|
|
49
|
-
> An action just settled for the current Boss turn; its outcome report — the settlement facts verbatim, the receipt disposition,
|
|
52
|
+
> An action just settled for the current Boss turn; its canonical outcome report — the settlement facts verbatim, the structured receipt disposition, any bounded terminal-result meaning, the leaf-state summary, and bounded repository-effect evidence — is supplied with this call.
|
|
50
53
|
> The closing reply is the turn summary: compose the closing reply and turn summary only from the outcome-report facts.
|
|
51
54
|
> State what actually happened — what was dismissed, started, delivered, applied, rejected, or failed — and claim no work the report does not contain.
|
|
55
|
+
> When repository-effect evidence is supplied, distinguish an observed repository change from a possible effect that could not be excluded, preserve its exact available HEAD and proven commit identity, and claim neither workflow completion nor ownership of the change.
|
|
52
56
|
> Do not finish with a bare acknowledgement, a promise to act, or an announcement that the round is complete.
|
|
53
57
|
> When mentioning progress detail, use only the aggregate counts the report supplies.
|
|
54
58
|
> Append the supplied saved-counts line verbatim only when one is supplied; when none is supplied, append no saved-counts line.
|
|
@@ -5,10 +5,13 @@ export type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, Pla
|
|
|
5
5
|
export type { DecisionAction, EnabledPlaybook, ParsedActingDecision, SettlementEvidence, SettlementReceiptEvidence, };
|
|
6
6
|
/** One nonempty complete standalone request selected for `start` or `switch`. */
|
|
7
7
|
export type CaptainControllerInput = string;
|
|
8
|
-
/** One validated controller selection submitted through the port (DR-029). */
|
|
8
|
+
/** One validated controller selection submitted through the port (DR-029, DR-038). */
|
|
9
9
|
export type CaptainControllerSelection = {
|
|
10
10
|
readonly action: 'respond';
|
|
11
11
|
readonly text: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly action: 'resume';
|
|
14
|
+
readonly playbookId: string;
|
|
12
15
|
} | {
|
|
13
16
|
readonly action: 'start' | 'switch';
|
|
14
17
|
readonly playbookId: string;
|
|
@@ -92,6 +95,7 @@ export declare const _internal: {
|
|
|
92
95
|
validateParsedActingDecision: typeof validateParsedActingDecision;
|
|
93
96
|
statusesForState: typeof statusesForState;
|
|
94
97
|
normalizeError: typeof normalizeError;
|
|
98
|
+
UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string>;
|
|
95
99
|
};
|
|
96
100
|
export declare function createPlaybookRuntime(options: PlaybookRuntimeOptions): PlaybookRuntime;
|
|
97
101
|
declare const factory: PlaybookRuntimeFactory<PlaybookRuntimeOptions>;
|