@sublang/playbook 17.4.0 → 17.5.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/docs/cli.md +30 -3
- package/docs/configuration.md +32 -2
- package/docs/embedding.md +29 -0
- package/package.json +2 -2
- package/reference/sdlc/code.md +24 -4
- package/reference/sdlc/code.playbook/bin/launch-config.js +249 -39
- package/reference/sdlc/code.playbook/bin/playbook.js +43 -6
- package/reference/sdlc/code.playbook/bin/run.js +37 -3
- package/reference/sdlc/code.playbook/bin/session-store.js +11 -5
- package/reference/sdlc/code.playbook/code.fsm.d.ts +12 -0
- package/reference/sdlc/code.playbook/code.fsm.js +40 -6
- package/reference/sdlc/code.playbook/code.fsm.ts +53 -6
- package/reference/sdlc/code.playbook/code.gears.md +32 -4
- package/reference/sdlc/code.playbook/playbook-captain.js +45 -4
- package/reference/sdlc/code.playbook/playbook-captain.ts +50 -3
- package/reference/sdlc/code.playbook/session-assets.js +7 -6
- package/reference/sdlc/code.playbook/session-host.d.ts +9 -2
- package/slc/gears2fsm.md +17 -3
- package/slc/text2gears.md +7 -0
- package/src/xstate-playbook-runtime.js +5 -0
- package/src/xstate-playbook-runtime.ts +7 -0
|
@@ -1161,12 +1161,18 @@ export function createCaptainSessionStore(options = {}) {
|
|
|
1161
1161
|
try {
|
|
1162
1162
|
await assertPrivateDirectory(assetsDirectory, fs);
|
|
1163
1163
|
const names = await fs.readdir(assetsDirectory);
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1164
|
+
const owned = names.filter((name) => /^(?:[0-9a-f]{64}(?:\.json)?|\.import-[0-9a-f-]+\.tmp)$/.test(name));
|
|
1165
|
+
for (const name of owned) await assertPrivateRegularPath(join(assetsDirectory, name), 0o600, fs, 'session asset');
|
|
1166
|
+
for (const name of owned) await fs.unlink(join(assetsDirectory, name));
|
|
1167
|
+
// Foreign regular files (Finder or editor files) go with the session; a
|
|
1168
|
+
// remaining subdirectory or link keeps the directory, not the session.
|
|
1169
|
+
for (const name of names.filter((entry) => !owned.includes(entry))) {
|
|
1170
|
+
const path = join(assetsDirectory, name);
|
|
1171
|
+
try { const stat = await fs.lstat(path); if (stat.isFile() && !stat.isSymbolicLink()) await fs.unlink(path); }
|
|
1172
|
+
catch (cause) { if (cause?.code !== 'ENOENT') throw cause; }
|
|
1167
1173
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1174
|
+
try { await fs.rmdir(assetsDirectory); }
|
|
1175
|
+
catch (cause) { if (cause?.code !== 'ENOTEMPTY' && cause?.code !== 'EEXIST') throw cause; }
|
|
1170
1176
|
await syncDirectory(sessionsDir, fs);
|
|
1171
1177
|
} catch (cause) { if (cause?.code !== 'ENOENT') throw cause; }
|
|
1172
1178
|
for (const suffix of ['.records.jsonl', '.hints.json', '.spex.json', '.json']) {
|
|
@@ -51,6 +51,8 @@ export interface IrTaskPhasePlayerInput extends PlayerInputBase {
|
|
|
51
51
|
readonly sourceItem: 'CODE-3';
|
|
52
52
|
/** `<ir-number>`: the IR whose next unfinished task this phase implements. */
|
|
53
53
|
readonly irNumber?: string;
|
|
54
|
+
/** Source-owned relay; historical canonical evidence, not present approval. */
|
|
55
|
+
readonly previousPhaseReview: string;
|
|
54
56
|
}
|
|
55
57
|
export type PlayerInput = FirstPhasePlayerInput | IrTaskPhasePlayerInput;
|
|
56
58
|
export type PlayerOutput = {
|
|
@@ -125,6 +127,14 @@ export interface CodeInput {
|
|
|
125
127
|
/** Optional seed for `<run-results>`; `START_CODE` may supply its own. */
|
|
126
128
|
readonly runResults?: string;
|
|
127
129
|
}
|
|
130
|
+
export interface PreviousPhaseReview {
|
|
131
|
+
readonly phaseKind: 'direct' | 'new-intent' | 'ir-task';
|
|
132
|
+
readonly phaseOutcome: PhaseOutcome;
|
|
133
|
+
readonly scopeCommit: string;
|
|
134
|
+
readonly evaluatedRevision: string;
|
|
135
|
+
readonly irNumber?: string;
|
|
136
|
+
readonly irTask?: string;
|
|
137
|
+
}
|
|
128
138
|
export interface CodeContext {
|
|
129
139
|
/** `<run-results>`: relevant run results relayed to every phase. */
|
|
130
140
|
readonly runResults: string;
|
|
@@ -142,6 +152,8 @@ export interface CodeContext {
|
|
|
142
152
|
readonly phaseOutcome?: PhaseOutcome;
|
|
143
153
|
/** Revision evaluated by the latest passing review. */
|
|
144
154
|
readonly evaluatedRevision?: string;
|
|
155
|
+
/** Frozen only by an accepted canonical clean child REVIEW. */
|
|
156
|
+
readonly previousPhaseReview?: PreviousPhaseReview;
|
|
145
157
|
/** Compact failure CODE reports when review did not pass a phase. */
|
|
146
158
|
readonly reviewError?: CompactError;
|
|
147
159
|
/** Sanitized evidence of the review result that ended CODE. */
|
|
@@ -25,13 +25,19 @@ const FIRST_PHASE_PROMPT = [
|
|
|
25
25
|
'For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
26
26
|
'Do not implement a later task in this phase.',
|
|
27
27
|
"Mark the IR's progress and deliverables when relevant.",
|
|
28
|
+
"After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.",
|
|
29
|
+
"Report pending independent REVIEW, owner acceptance and later delivery/workshop steps separately; they imply an unfinished IR task only when the IR assigns that work to a remaining task.",
|
|
30
|
+
"Report only what the IR and completed work establish, in ordinary prose without a required marker.",
|
|
28
31
|
'If the IR will be finished after this phase, double-check that all acceptance criteria are met.',
|
|
29
32
|
'',
|
|
30
33
|
'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
|
|
31
34
|
'',
|
|
32
35
|
'Keep to the original intent and follow what it asks.',
|
|
33
36
|
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
34
|
-
"
|
|
37
|
+
"If a required Boss answer prevents completion of the current phase or IR task, ask that question and return without committing.",
|
|
38
|
+
"Preserve permitted uncommitted work for the deferred continuation; do not create a partial or blocker-only commit to meet the one-commit requirement.",
|
|
39
|
+
"After the answer allows completion, make exactly one phase-owned commit before REVIEW.",
|
|
40
|
+
"When the phase is complete, make its minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
|
|
35
41
|
'Make the commit message explain concisely what changed and why, including relevant verification.',
|
|
36
42
|
'Identify every new commit you make.',
|
|
37
43
|
'Credit every AI that contributed to this commit: Coder <coder-llm>.',
|
|
@@ -43,11 +49,20 @@ const IR_TASK_PHASE_PROMPT = [
|
|
|
43
49
|
'Read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
44
50
|
'Do not implement a later task in this phase.',
|
|
45
51
|
"Mark the IR's progress and deliverables when relevant.",
|
|
52
|
+
"After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.",
|
|
53
|
+
"Report pending independent REVIEW, owner acceptance and later delivery/workshop steps separately; they imply an unfinished IR task only when the IR assigns that work to a remaining task.",
|
|
54
|
+
"Report only what the IR and completed work establish, in ordinary prose without a required marker.",
|
|
46
55
|
'If the IR will be finished after this phase, double-check that all acceptance criteria are met.',
|
|
56
|
+
'Use the quoted previous-phase review only for its exact recorded scope; committed pending-review text may predate that canonical result.',
|
|
57
|
+
'Before treating it as current review evidence, verify clean current HEAD equals its evaluatedRevision; a mismatch is not approval.',
|
|
58
|
+
'It does not replace independent review of this task or any new owner or release decision.',
|
|
47
59
|
'',
|
|
48
60
|
'Keep to the original intent and follow what it asks.',
|
|
49
61
|
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
50
|
-
"
|
|
62
|
+
"If a required Boss answer prevents completion of the current phase or IR task, ask that question and return without committing.",
|
|
63
|
+
"Preserve permitted uncommitted work for the deferred continuation; do not create a partial or blocker-only commit to meet the one-commit requirement.",
|
|
64
|
+
"After the answer allows completion, make exactly one phase-owned commit before REVIEW.",
|
|
65
|
+
"When the phase is complete, make its minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
|
|
51
66
|
'Make the commit message explain concisely what changed and why, including relevant verification.',
|
|
52
67
|
'Identify every new commit you make.',
|
|
53
68
|
'Credit every AI that contributed to this commit: Coder <coder-llm>.',
|
|
@@ -55,6 +70,7 @@ const IR_TASK_PHASE_PROMPT = [
|
|
|
55
70
|
'> Original request: <caller-input>',
|
|
56
71
|
'> IR number: <ir-number>',
|
|
57
72
|
'> Run results: <run-results>',
|
|
73
|
+
'> Previous phase review: <previous-phase-review>',
|
|
58
74
|
].join('\n');
|
|
59
75
|
const REVIEW_NEW_INTENT_PHASE_TEMPLATE = [
|
|
60
76
|
'> Original intent: <caller-input>',
|
|
@@ -68,7 +84,7 @@ const REVIEW_IR_TASK_PHASE_TEMPLATE = [
|
|
|
68
84
|
'',
|
|
69
85
|
'> Current IR task: <ir-task>',
|
|
70
86
|
].join('\n');
|
|
71
|
-
const NEEDS_BOSS_REPLY_DESCRIPTION = "
|
|
87
|
+
const NEEDS_BOSS_REPLY_DESCRIPTION = "At the end of this call, Coder has an unanswered clarifying question for Boss whose answer is required to complete the current coding phase. Earlier interim questions resolved or superseded by later affirmative current-phase completion, and prerequisites only for later review, acceptance or release stages, do not establish this outcome. Output shall include `question: <verbatim question text from the acting agent's prose>`.";
|
|
72
88
|
const FIRST_PHASE_RESULT = {
|
|
73
89
|
directCommit: "Coder completed the new coding intent as one direct implementation phase and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
|
|
74
90
|
irCommit: "Coder decomposed the new coding intent into a new IR, implemented no IR task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irNumber` identifying the created IR.",
|
|
@@ -467,6 +483,7 @@ export const codeMachine = setup({
|
|
|
467
483
|
irTask: undefined,
|
|
468
484
|
phaseOutcome: undefined,
|
|
469
485
|
evaluatedRevision: undefined,
|
|
486
|
+
previousPhaseReview: undefined,
|
|
470
487
|
reviewError: undefined,
|
|
471
488
|
reviewEvidence: undefined,
|
|
472
489
|
lastError: undefined,
|
|
@@ -477,6 +494,7 @@ export const codeMachine = setup({
|
|
|
477
494
|
resetForInterrupt: assign({
|
|
478
495
|
phaseOutcome: undefined,
|
|
479
496
|
evaluatedRevision: undefined,
|
|
497
|
+
previousPhaseReview: undefined,
|
|
480
498
|
reviewError: undefined,
|
|
481
499
|
reviewEvidence: undefined,
|
|
482
500
|
lastError: undefined,
|
|
@@ -516,11 +534,24 @@ export const codeMachine = setup({
|
|
|
516
534
|
pendingBossQuestion: undefined,
|
|
517
535
|
bossReply: undefined,
|
|
518
536
|
}),
|
|
519
|
-
rememberReviewPass: assign(({ event }) => {
|
|
537
|
+
rememberReviewPass: assign(({ context, event }) => {
|
|
520
538
|
const pass = reviewPassOf(event);
|
|
521
|
-
return pass === undefined
|
|
539
|
+
return pass === undefined || context.codeCommit === undefined || context.phaseOutcome === undefined
|
|
522
540
|
? {}
|
|
523
|
-
: {
|
|
541
|
+
: {
|
|
542
|
+
evaluatedRevision: pass.evaluatedRevision,
|
|
543
|
+
previousPhaseReview: {
|
|
544
|
+
phaseKind: context.phaseOutcome === 'directCommit'
|
|
545
|
+
? 'direct' : context.phaseOutcome === 'irCommit'
|
|
546
|
+
? 'new-intent' : 'ir-task',
|
|
547
|
+
phaseOutcome: context.phaseOutcome,
|
|
548
|
+
scopeCommit: context.codeCommit,
|
|
549
|
+
evaluatedRevision: pass.evaluatedRevision,
|
|
550
|
+
...(context.irNumber === undefined ? {} : { irNumber: context.irNumber }),
|
|
551
|
+
...(context.irTask === undefined ? {} : { irTask: context.irTask }),
|
|
552
|
+
},
|
|
553
|
+
lastError: undefined,
|
|
554
|
+
};
|
|
524
555
|
}),
|
|
525
556
|
rememberReviewNotPassed: assign(({ event }) => {
|
|
526
557
|
const output = doneOutputOf(event);
|
|
@@ -703,6 +734,9 @@ export const codeMachine = setup({
|
|
|
703
734
|
? { irNumber: context.irNumber }
|
|
704
735
|
: {}),
|
|
705
736
|
runResults: context.runResults,
|
|
737
|
+
previousPhaseReview: context.previousPhaseReview === undefined
|
|
738
|
+
? 'No accepted prior-phase review is available.'
|
|
739
|
+
: JSON.stringify(context.previousPhaseReview),
|
|
706
740
|
...bossReplyFields(context),
|
|
707
741
|
}),
|
|
708
742
|
onDone: [
|
|
@@ -95,6 +95,8 @@ export interface IrTaskPhasePlayerInput extends PlayerInputBase {
|
|
|
95
95
|
readonly sourceItem: 'CODE-3';
|
|
96
96
|
/** `<ir-number>`: the IR whose next unfinished task this phase implements. */
|
|
97
97
|
readonly irNumber?: string;
|
|
98
|
+
/** Source-owned relay; historical canonical evidence, not present approval. */
|
|
99
|
+
readonly previousPhaseReview: string;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
export type PlayerInput = FirstPhasePlayerInput | IrTaskPhasePlayerInput;
|
|
@@ -194,6 +196,15 @@ export interface CodeInput {
|
|
|
194
196
|
readonly runResults?: string;
|
|
195
197
|
}
|
|
196
198
|
|
|
199
|
+
export interface PreviousPhaseReview {
|
|
200
|
+
readonly phaseKind: 'direct' | 'new-intent' | 'ir-task';
|
|
201
|
+
readonly phaseOutcome: PhaseOutcome;
|
|
202
|
+
readonly scopeCommit: string;
|
|
203
|
+
readonly evaluatedRevision: string;
|
|
204
|
+
readonly irNumber?: string;
|
|
205
|
+
readonly irTask?: string;
|
|
206
|
+
}
|
|
207
|
+
|
|
197
208
|
export interface CodeContext {
|
|
198
209
|
/** `<run-results>`: relevant run results relayed to every phase. */
|
|
199
210
|
readonly runResults: string;
|
|
@@ -211,6 +222,8 @@ export interface CodeContext {
|
|
|
211
222
|
readonly phaseOutcome?: PhaseOutcome;
|
|
212
223
|
/** Revision evaluated by the latest passing review. */
|
|
213
224
|
readonly evaluatedRevision?: string;
|
|
225
|
+
/** Frozen only by an accepted canonical clean child REVIEW. */
|
|
226
|
+
readonly previousPhaseReview?: PreviousPhaseReview;
|
|
214
227
|
/** Compact failure CODE reports when review did not pass a phase. */
|
|
215
228
|
readonly reviewError?: CompactError;
|
|
216
229
|
/** Sanitized evidence of the review result that ended CODE. */
|
|
@@ -249,13 +262,19 @@ const FIRST_PHASE_PROMPT = [
|
|
|
249
262
|
'For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
250
263
|
'Do not implement a later task in this phase.',
|
|
251
264
|
"Mark the IR's progress and deliverables when relevant.",
|
|
265
|
+
"After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.",
|
|
266
|
+
"Report pending independent REVIEW, owner acceptance and later delivery/workshop steps separately; they imply an unfinished IR task only when the IR assigns that work to a remaining task.",
|
|
267
|
+
"Report only what the IR and completed work establish, in ordinary prose without a required marker.",
|
|
252
268
|
'If the IR will be finished after this phase, double-check that all acceptance criteria are met.',
|
|
253
269
|
'',
|
|
254
270
|
'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
|
|
255
271
|
'',
|
|
256
272
|
'Keep to the original intent and follow what it asks.',
|
|
257
273
|
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
258
|
-
"
|
|
274
|
+
"If a required Boss answer prevents completion of the current phase or IR task, ask that question and return without committing.",
|
|
275
|
+
"Preserve permitted uncommitted work for the deferred continuation; do not create a partial or blocker-only commit to meet the one-commit requirement.",
|
|
276
|
+
"After the answer allows completion, make exactly one phase-owned commit before REVIEW.",
|
|
277
|
+
"When the phase is complete, make its minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
|
|
259
278
|
'Make the commit message explain concisely what changed and why, including relevant verification.',
|
|
260
279
|
'Identify every new commit you make.',
|
|
261
280
|
'Credit every AI that contributed to this commit: Coder <coder-llm>.',
|
|
@@ -268,11 +287,20 @@ const IR_TASK_PHASE_PROMPT = [
|
|
|
268
287
|
'Read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.',
|
|
269
288
|
'Do not implement a later task in this phase.',
|
|
270
289
|
"Mark the IR's progress and deliverables when relevant.",
|
|
290
|
+
"After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.",
|
|
291
|
+
"Report pending independent REVIEW, owner acceptance and later delivery/workshop steps separately; they imply an unfinished IR task only when the IR assigns that work to a remaining task.",
|
|
292
|
+
"Report only what the IR and completed work establish, in ordinary prose without a required marker.",
|
|
271
293
|
'If the IR will be finished after this phase, double-check that all acceptance criteria are met.',
|
|
294
|
+
'Use the quoted previous-phase review only for its exact recorded scope; committed pending-review text may predate that canonical result.',
|
|
295
|
+
'Before treating it as current review evidence, verify clean current HEAD equals its evaluatedRevision; a mismatch is not approval.',
|
|
296
|
+
'It does not replace independent review of this task or any new owner or release decision.',
|
|
272
297
|
'',
|
|
273
298
|
'Keep to the original intent and follow what it asks.',
|
|
274
299
|
'Do not re-run tests or builds whose inputs have not changed since any previous reported run.',
|
|
275
|
-
"
|
|
300
|
+
"If a required Boss answer prevents completion of the current phase or IR task, ask that question and return without committing.",
|
|
301
|
+
"Preserve permitted uncommitted work for the deferred continuation; do not create a partial or blocker-only commit to meet the one-commit requirement.",
|
|
302
|
+
"After the answer allows completion, make exactly one phase-owned commit before REVIEW.",
|
|
303
|
+
"When the phase is complete, make its minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.",
|
|
276
304
|
'Make the commit message explain concisely what changed and why, including relevant verification.',
|
|
277
305
|
'Identify every new commit you make.',
|
|
278
306
|
'Credit every AI that contributed to this commit: Coder <coder-llm>.',
|
|
@@ -280,6 +308,7 @@ const IR_TASK_PHASE_PROMPT = [
|
|
|
280
308
|
'> Original request: <caller-input>',
|
|
281
309
|
'> IR number: <ir-number>',
|
|
282
310
|
'> Run results: <run-results>',
|
|
311
|
+
'> Previous phase review: <previous-phase-review>',
|
|
283
312
|
].join('\n');
|
|
284
313
|
|
|
285
314
|
const REVIEW_NEW_INTENT_PHASE_TEMPLATE = [
|
|
@@ -297,7 +326,7 @@ const REVIEW_IR_TASK_PHASE_TEMPLATE = [
|
|
|
297
326
|
].join('\n');
|
|
298
327
|
|
|
299
328
|
const NEEDS_BOSS_REPLY_DESCRIPTION =
|
|
300
|
-
"
|
|
329
|
+
"At the end of this call, Coder has an unanswered clarifying question for Boss whose answer is required to complete the current coding phase. Earlier interim questions resolved or superseded by later affirmative current-phase completion, and prerequisites only for later review, acceptance or release stages, do not establish this outcome. Output shall include `question: <verbatim question text from the acting agent's prose>`.";
|
|
301
330
|
|
|
302
331
|
const FIRST_PHASE_RESULT = {
|
|
303
332
|
directCommit:
|
|
@@ -823,6 +852,7 @@ export const codeMachine = setup({
|
|
|
823
852
|
irTask: undefined,
|
|
824
853
|
phaseOutcome: undefined,
|
|
825
854
|
evaluatedRevision: undefined,
|
|
855
|
+
previousPhaseReview: undefined,
|
|
826
856
|
reviewError: undefined,
|
|
827
857
|
reviewEvidence: undefined,
|
|
828
858
|
lastError: undefined,
|
|
@@ -834,6 +864,7 @@ export const codeMachine = setup({
|
|
|
834
864
|
resetForInterrupt: assign({
|
|
835
865
|
phaseOutcome: undefined,
|
|
836
866
|
evaluatedRevision: undefined,
|
|
867
|
+
previousPhaseReview: undefined,
|
|
837
868
|
reviewError: undefined,
|
|
838
869
|
reviewEvidence: undefined,
|
|
839
870
|
lastError: undefined,
|
|
@@ -876,11 +907,24 @@ export const codeMachine = setup({
|
|
|
876
907
|
pendingBossQuestion: undefined,
|
|
877
908
|
bossReply: undefined,
|
|
878
909
|
}),
|
|
879
|
-
rememberReviewPass: assign(({ event }) => {
|
|
910
|
+
rememberReviewPass: assign(({ context, event }) => {
|
|
880
911
|
const pass = reviewPassOf(event);
|
|
881
|
-
return pass === undefined
|
|
912
|
+
return pass === undefined || context.codeCommit === undefined || context.phaseOutcome === undefined
|
|
882
913
|
? {}
|
|
883
|
-
: {
|
|
914
|
+
: {
|
|
915
|
+
evaluatedRevision: pass.evaluatedRevision,
|
|
916
|
+
previousPhaseReview: {
|
|
917
|
+
phaseKind: context.phaseOutcome === 'directCommit'
|
|
918
|
+
? 'direct' as const : context.phaseOutcome === 'irCommit'
|
|
919
|
+
? 'new-intent' as const : 'ir-task' as const,
|
|
920
|
+
phaseOutcome: context.phaseOutcome,
|
|
921
|
+
scopeCommit: context.codeCommit,
|
|
922
|
+
evaluatedRevision: pass.evaluatedRevision,
|
|
923
|
+
...(context.irNumber === undefined ? {} : { irNumber: context.irNumber }),
|
|
924
|
+
...(context.irTask === undefined ? {} : { irTask: context.irTask }),
|
|
925
|
+
},
|
|
926
|
+
lastError: undefined,
|
|
927
|
+
};
|
|
884
928
|
}),
|
|
885
929
|
rememberReviewNotPassed: assign(({ event }) => {
|
|
886
930
|
const output = doneOutputOf(event);
|
|
@@ -1086,6 +1130,9 @@ export const codeMachine = setup({
|
|
|
1086
1130
|
? { irNumber: context.irNumber }
|
|
1087
1131
|
: {}),
|
|
1088
1132
|
runResults: context.runResults,
|
|
1133
|
+
previousPhaseReview: context.previousPhaseReview === undefined
|
|
1134
|
+
? 'No accepted prior-phase review is available.'
|
|
1135
|
+
: JSON.stringify(context.previousPhaseReview),
|
|
1089
1136
|
...bossReplyFields(context),
|
|
1090
1137
|
}),
|
|
1091
1138
|
onDone: [
|
|
@@ -11,7 +11,7 @@ Roles:
|
|
|
11
11
|
|
|
12
12
|
### CODE-1
|
|
13
13
|
|
|
14
|
-
Each phase ends with exactly one new Coder commit owned by `code`, and no existing commit is rewritten.
|
|
14
|
+
Each completed phase ends with exactly one new Coder commit owned by `code`, and no existing commit is rewritten.
|
|
15
15
|
Captain uses the repository-effect receipt as the authoritative identity of the phase's new commit.
|
|
16
16
|
When the request may continue an existing IR that it does not identify unambiguously, Coder asks Boss before changing files, and Boss's answer resumes this same phase.
|
|
17
17
|
|
|
@@ -28,13 +28,19 @@ When the first coding phase begins for the caller's coding request and any relev
|
|
|
28
28
|
> For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
29
29
|
> Do not implement a later task in this phase.
|
|
30
30
|
> Mark the IR's progress and deliverables when relevant.
|
|
31
|
+
> After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.
|
|
32
|
+
> Report pending independent REVIEW, owner acceptance and later delivery/workshop steps separately; they imply an unfinished IR task only when the IR assigns that work to a remaining task.
|
|
33
|
+
> Report only what the IR and completed work establish, in ordinary prose without a required marker.
|
|
31
34
|
> If the IR will be finished after this phase, double-check that all acceptance criteria are met.
|
|
32
35
|
>
|
|
33
36
|
> Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.
|
|
34
37
|
>
|
|
35
38
|
> Keep to the original intent and follow what it asks.
|
|
36
39
|
> Do not re-run tests or builds whose inputs have not changed since any previous reported run.
|
|
37
|
-
>
|
|
40
|
+
> If a required Boss answer prevents completion of the current phase or IR task, ask that question and return without committing.
|
|
41
|
+
> Preserve permitted uncommitted work for the deferred continuation; do not create a partial or blocker-only commit to meet the one-commit requirement.
|
|
42
|
+
> After the answer allows completion, make exactly one phase-owned commit before REVIEW.
|
|
43
|
+
> When the phase is complete, make its minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.
|
|
38
44
|
> Make the commit message explain concisely what changed and why, including relevant verification.
|
|
39
45
|
> Identify every new commit you make.
|
|
40
46
|
> Credit every AI that contributed to this commit: Coder <coder-llm>.
|
|
@@ -47,6 +53,7 @@ Results:
|
|
|
47
53
|
- `irCommit`: Coder decomposed the new coding intent into a new IR, implemented no IR task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, and `irNumber` identifying the created IR.
|
|
48
54
|
- `moreTasks`: Coder continued an existing IR, implemented exactly its next unfinished task, made its one new commit, and at least one IR task remains unfinished; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.
|
|
49
55
|
- `finalTask`: Coder continued an existing IR, implemented its final unfinished task, and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the continued IR, and `irTask` naming the implemented task.
|
|
56
|
+
- `needsBossReply`: At the end of this call, Coder has an unanswered clarifying question for Boss whose answer is required to complete the current coding phase. Earlier interim questions resolved or superseded by later affirmative current-phase completion, and prerequisites only for later review, acceptance or release stages, do not establish this outcome. Output shall include `question: <verbatim question text from the acting agent's prose>`.
|
|
50
57
|
|
|
51
58
|
### CODE-2
|
|
52
59
|
|
|
@@ -59,6 +66,10 @@ When a direct implementation or new-IR phase ends with its one new `code`-owned
|
|
|
59
66
|
> > Coder output: <coder-output>
|
|
60
67
|
|
|
61
68
|
A nested `review` passes the phase only when its result applies to that supplied review scope, returns the exact evaluated repository revision, and affirmatively establishes that no unsettled findings remain.
|
|
69
|
+
On each successful nested REVIEW, Captain shall freeze the previous phase review as one record from the canonical child result and the phase's accepted receipt-owned scope: exact phase kind (direct, new intent or IR task), phase outcome, CODE scope commit, REVIEW evaluated revision, and IR/task identity when present.
|
|
70
|
+
CODE scope commit and evaluated revision remain distinct because REVIEW may create fix commits.
|
|
71
|
+
No caller option, player prose or repository status text supplies this record.
|
|
72
|
+
The record survives permitted restoration and the current task's Boss-answer continuation, and a fresh run or explicit Boss interruption clears it.
|
|
62
73
|
When `review` passes a direct implementation phase, `code` is complete and returns to its caller the exact last `code`-owned commit, the exact final evaluated repository revision, and the fact that every phase's review passed with no unsettled findings.
|
|
63
74
|
When `review` passes a new-IR phase, Captain continues with the next unfinished IR-task phase.
|
|
64
75
|
When `review` returns an authored abort or failure, or a terminal result that does not establish that the supplied scope was evaluated with no unsettled findings, `code` starts no further phase and reports the failure and the last `code`-owned commit to its caller.
|
|
@@ -66,7 +77,9 @@ When the nested `review` call fails outside that authored result contract, `code
|
|
|
66
77
|
|
|
67
78
|
### CODE-3
|
|
68
79
|
|
|
69
|
-
|
|
80
|
+
When absent, the previous phase review reads "No accepted prior-phase review is available.".
|
|
81
|
+
|
|
82
|
+
Each completed phase ends with exactly one new Coder commit owned by `code`, and no existing commit is rewritten.
|
|
70
83
|
Captain uses the repository-effect receipt as the authoritative identity of the phase's new commit.
|
|
71
84
|
|
|
72
85
|
When a later IR-task phase begins, Captain shall prompt Coder:
|
|
@@ -74,11 +87,20 @@ When a later IR-task phase begins, Captain shall prompt Coder:
|
|
|
74
87
|
> Read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
75
88
|
> Do not implement a later task in this phase.
|
|
76
89
|
> Mark the IR's progress and deliverables when relevant.
|
|
90
|
+
> After implementing an IR task, identify it and state whether any IR task still has unfinished Coder work; if so identify the next unfinished task, otherwise state this was the final IR task's Coder work.
|
|
91
|
+
> Report pending independent REVIEW, owner acceptance and later delivery/workshop steps separately; they imply an unfinished IR task only when the IR assigns that work to a remaining task.
|
|
92
|
+
> Report only what the IR and completed work establish, in ordinary prose without a required marker.
|
|
77
93
|
> If the IR will be finished after this phase, double-check that all acceptance criteria are met.
|
|
94
|
+
> Use the quoted previous-phase review only for its exact recorded scope; committed pending-review text may predate that canonical result.
|
|
95
|
+
> Before treating it as current review evidence, verify clean current HEAD equals its evaluatedRevision; a mismatch is not approval.
|
|
96
|
+
> It does not replace independent review of this task or any new owner or release decision.
|
|
78
97
|
>
|
|
79
98
|
> Keep to the original intent and follow what it asks.
|
|
80
99
|
> Do not re-run tests or builds whose inputs have not changed since any previous reported run.
|
|
81
|
-
>
|
|
100
|
+
> If a required Boss answer prevents completion of the current phase or IR task, ask that question and return without committing.
|
|
101
|
+
> Preserve permitted uncommitted work for the deferred continuation; do not create a partial or blocker-only commit to meet the one-commit requirement.
|
|
102
|
+
> After the answer allows completion, make exactly one phase-owned commit before REVIEW.
|
|
103
|
+
> When the phase is complete, make its minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.
|
|
82
104
|
> Make the commit message explain concisely what changed and why, including relevant verification.
|
|
83
105
|
> Identify every new commit you make.
|
|
84
106
|
> Credit every AI that contributed to this commit: Coder <coder-llm>.
|
|
@@ -86,10 +108,12 @@ When a later IR-task phase begins, Captain shall prompt Coder:
|
|
|
86
108
|
> > Original request: <caller-input>
|
|
87
109
|
> > IR number: <ir-number>
|
|
88
110
|
> > Run results: <run-results>
|
|
111
|
+
> > Previous phase review: <previous-phase-review>
|
|
89
112
|
|
|
90
113
|
Results:
|
|
91
114
|
- `moreTasks`: Coder implemented exactly the IR's next unfinished task, made its one new commit, and at least one IR task remains unfinished; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the IR, and `irTask` naming the implemented task.
|
|
92
115
|
- `finalTask`: Coder implemented the IR's final unfinished task and made its one new commit; Coder's result affirmatively supports this outcome, and no fixed presentation format of the reply is required. Output shall include `coderOutput: <verbatim final text>`, `latestCommit: <commit identity>`, `irNumber` identifying the IR, and `irTask` naming the implemented task.
|
|
116
|
+
- `needsBossReply`: At the end of this call, Coder has an unanswered clarifying question for Boss whose answer is required to complete the current coding phase. Earlier interim questions resolved or superseded by later affirmative current-phase completion, and prerequisites only for later review, acceptance or release stages, do not establish this outcome. Output shall include `question: <verbatim question text from the acting agent's prose>`.
|
|
93
117
|
|
|
94
118
|
### CODE-4
|
|
95
119
|
|
|
@@ -104,6 +128,10 @@ When an IR-task phase, including the first phase for an existing IR, ends with i
|
|
|
104
128
|
> > Current IR task: <ir-task>
|
|
105
129
|
|
|
106
130
|
A nested `review` passes the phase only when its result applies to that supplied review scope, returns the exact evaluated repository revision, and affirmatively establishes that no unsettled findings remain.
|
|
131
|
+
On each successful nested REVIEW, Captain shall freeze the previous phase review as one record from the canonical child result and the phase's accepted receipt-owned scope: exact phase kind (direct, new intent or IR task), phase outcome, CODE scope commit, REVIEW evaluated revision, and IR/task identity when present.
|
|
132
|
+
CODE scope commit and evaluated revision remain distinct because REVIEW may create fix commits.
|
|
133
|
+
No caller option, player prose or repository status text supplies this record.
|
|
134
|
+
The record survives permitted restoration and the current task's Boss-answer continuation, and a fresh run or explicit Boss interruption clears it.
|
|
107
135
|
When `review` passes a nonfinal IR-task phase, Captain continues with the next unfinished IR-task phase.
|
|
108
136
|
When `review` passes the final IR-task phase, `code` is complete and returns to its caller the exact last `code`-owned commit, the exact final evaluated repository revision, and the fact that every phase's review passed with no unsettled findings.
|
|
109
137
|
When `review` returns an authored abort or failure, or a terminal result that does not establish that the supplied scope was evaluated with no unsettled findings, `code` starts no further phase and reports the failure and the last `code`-owned commit to its caller.
|
|
@@ -24,6 +24,15 @@ function retainedEffectLedgerCanRebase(checkpoint, current) {
|
|
|
24
24
|
.slice(checkpoint.boundaries.length)
|
|
25
25
|
.every(({ physicalReceipt }) => physicalReceipt?.classification === 'unchanged');
|
|
26
26
|
}
|
|
27
|
+
function missingPackagedReview(enablementById, playbookId) {
|
|
28
|
+
const selected = enablementById.get(playbookId);
|
|
29
|
+
if (selected === undefined ||
|
|
30
|
+
enablementById.has('review') ||
|
|
31
|
+
!['code', 'decide'].includes(playbookId) ||
|
|
32
|
+
selected.configuredFrom !== `@sublang/playbook/${playbookId}/registry`)
|
|
33
|
+
return undefined;
|
|
34
|
+
return `/${selected.command} requires enabled playbook "review"; enable REVIEW before starting this packaged workflow`;
|
|
35
|
+
}
|
|
27
36
|
function createRuntimeForEnablement(enablement, hostCapabilitiesById) {
|
|
28
37
|
const hostCapabilities = hostCapabilitiesById.get(enablement.entry.id);
|
|
29
38
|
if (hostCapabilities === undefined) {
|
|
@@ -2363,6 +2372,7 @@ async function buildEnablements(options, loadModule, hostCapabilities) {
|
|
|
2363
2372
|
hostCapabilitiesById.set(entry.id, hostCapability);
|
|
2364
2373
|
enablementById.set(entry.id, {
|
|
2365
2374
|
entry,
|
|
2375
|
+
configuredFrom: from,
|
|
2366
2376
|
artifactSchema,
|
|
2367
2377
|
command,
|
|
2368
2378
|
options: validatedOptions,
|
|
@@ -3842,13 +3852,16 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
3842
3852
|
const sessionId = allocateSessionId();
|
|
3843
3853
|
const playerBindings = makePlayerBindings(enablement);
|
|
3844
3854
|
const runtime = createRuntimeForEnablement(enablement, hostCapabilitiesById);
|
|
3855
|
+
// A nested child inherits only its parent's selection, never the turn's
|
|
3856
|
+
// unselected references; only a root reads the turn's selection.
|
|
3857
|
+
const inherited = parent ? parent.frame.attachments : (activeTurn?.selectedAttachments ?? activeTurn?.attachments);
|
|
3845
3858
|
return {
|
|
3846
3859
|
entry,
|
|
3847
3860
|
enablement,
|
|
3848
3861
|
runtime,
|
|
3849
3862
|
sessionId,
|
|
3850
3863
|
rootSessionId: parent?.frame.rootSessionId ?? sessionId,
|
|
3851
|
-
...(
|
|
3864
|
+
...(inherited?.length ? { attachments: [...inherited] } : {}),
|
|
3852
3865
|
depth: parent ? parent.frame.depth + 1 : 0,
|
|
3853
3866
|
playerBindings,
|
|
3854
3867
|
...(parent ? { parent } : {}),
|
|
@@ -4152,8 +4165,6 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
4152
4165
|
finally {
|
|
4153
4166
|
if (leafFrame() === frame) {
|
|
4154
4167
|
frames.pop();
|
|
4155
|
-
if (!frame.parent)
|
|
4156
|
-
pendingAttachments = [];
|
|
4157
4168
|
if (frame.parent) {
|
|
4158
4169
|
pendingChildParents.delete(frame.parent.frame);
|
|
4159
4170
|
}
|
|
@@ -4541,6 +4552,24 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
4541
4552
|
return;
|
|
4542
4553
|
}
|
|
4543
4554
|
assertRetainableResult(frame, result);
|
|
4555
|
+
// A deferred checkpoint refusal changes the runtime's control overlay
|
|
4556
|
+
// without publishing an FSM transition. Refresh its derived question
|
|
4557
|
+
// mirror from an exact safe snapshot before reporting or saving the stop.
|
|
4558
|
+
if (leafFrame() === frame && !frame.disposing &&
|
|
4559
|
+
retainedEffectReconciliation === undefined) {
|
|
4560
|
+
try {
|
|
4561
|
+
const exported = frame.runtime.exportSnapshot?.();
|
|
4562
|
+
if (exported !== undefined) {
|
|
4563
|
+
const snapshot = assertPlaybookRuntimeSnapshot(exported, frame.entry.id);
|
|
4564
|
+
if (isDeepStrictEqual(snapshot.state, result.state) &&
|
|
4565
|
+
isDeepStrictEqual(snapshot.effectLedger, currentEffectLedger())) {
|
|
4566
|
+
pendingBossQuestions = snapshot.pendingBossQuestions.length === 0 && pendingBossQuestions === undefined
|
|
4567
|
+
? undefined : mirroredBossQuestions(snapshot.pendingBossQuestions);
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
catch { /* An inconsistent runtime must still fail exact capture. */ }
|
|
4572
|
+
}
|
|
4544
4573
|
if (result.outcome === 'aborted' && runFailureFacts) {
|
|
4545
4574
|
runFailureFacts.push(`${frameLabel(frame)} was aborted before its outcome could be confirmed; it was not repeated automatically.`);
|
|
4546
4575
|
}
|
|
@@ -4610,6 +4639,9 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
4610
4639
|
request.playbookId,
|
|
4611
4640
|
].join(' -> ')}`);
|
|
4612
4641
|
}
|
|
4642
|
+
const reviewRefusal = missingPackagedReview(enablementById, entry.id);
|
|
4643
|
+
if (reviewRefusal !== undefined)
|
|
4644
|
+
throw new Error(reviewRefusal);
|
|
4613
4645
|
pendingChildParents.add(parent);
|
|
4614
4646
|
let child;
|
|
4615
4647
|
try {
|
|
@@ -6245,7 +6277,13 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
6245
6277
|
// selections freeze after their work and before reporting begins.
|
|
6246
6278
|
if (selection.action === 'respond')
|
|
6247
6279
|
freezeControllerEvidence();
|
|
6248
|
-
|
|
6280
|
+
signal.throwIfAborted();
|
|
6281
|
+
const reviewRefusal = selection.action === 'start' || selection.action === 'switch'
|
|
6282
|
+
? missingPackagedReview(enablementById, selection.playbookId)
|
|
6283
|
+
: undefined;
|
|
6284
|
+
let settlement = reviewRefusal === undefined
|
|
6285
|
+
? await executeSelection(selection, signal, automaticRecovery)
|
|
6286
|
+
: await rejectSelection(selection, reviewRefusal);
|
|
6249
6287
|
// One controller action can include bounded prerequisite recovery.
|
|
6250
6288
|
// Neither the model nor a retry manufactures an unadvertised transition.
|
|
6251
6289
|
if (turn && abortPreparation && settlement.status !== 'rejected' && !['respond', 'dismiss', 'recover'].includes(selection.action)) {
|
|
@@ -6723,6 +6761,9 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
6723
6761
|
const nextAttachments = mergeAttachments(leaf.attachments ?? [], selected);
|
|
6724
6762
|
if (nextAttachments.length)
|
|
6725
6763
|
leaf.attachments = nextAttachments;
|
|
6764
|
+
// Delivered references now belong to the leaf; the rest stay pending.
|
|
6765
|
+
const delivered = new Set(selected.map((reference) => reference.assetId));
|
|
6766
|
+
pendingAttachments = pendingAttachments.filter((reference) => !delivered.has(reference.assetId));
|
|
6726
6767
|
// CAPTAIN-8: delivery carries text only, and the shell is authoritative
|
|
6727
6768
|
// for that text — any text carried on the selection is ignored.
|
|
6728
6769
|
turn.settled = true;
|