@sublang/playbook 17.4.0 → 17.4.1
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/package.json +2 -2
- package/reference/sdlc/code.md +24 -4
- 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/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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sublang/playbook",
|
|
3
|
-
"version": "17.4.
|
|
3
|
+
"version": "17.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composable XState v5 playbook runtime with compiled Captain, CODE, REVIEW, DECIDE, DEV, BRANCH, PR, and INSPECT workflows driven by GEARS specs.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -272,7 +272,7 @@
|
|
|
272
272
|
"provenance": true
|
|
273
273
|
},
|
|
274
274
|
"dependencies": {
|
|
275
|
-
"@sublang/cligent": "^0.33.
|
|
275
|
+
"@sublang/cligent": "^0.33.2",
|
|
276
276
|
"@sublang/spex": "^3.0.0",
|
|
277
277
|
"p-queue": "^9.3.1",
|
|
278
278
|
"xstate": "^5.19.4",
|
package/reference/sdlc/code.md
CHANGED
|
@@ -12,7 +12,7 @@ The caller supplies a coding request together with any relevant context.
|
|
|
12
12
|
|
|
13
13
|
A new coding intent follows either one direct implementation phase or an IR sequence consisting of a new-IR phase followed by one phase for each IR task.
|
|
14
14
|
An existing IR follows one IR-task phase for each remaining unfinished task, starting with its next unfinished task.
|
|
15
|
-
Each phase ends with one new Coder commit (owned by `code`).
|
|
15
|
+
Each completed phase ends with one new Coder commit (owned by `code`).
|
|
16
16
|
After each `code`-owned commit, Captain shall call the `review` playbook and wait until it passes with no unsettled findings.
|
|
17
17
|
Playbook `review` owns every review round and every review-fix commit.
|
|
18
18
|
Do not rewrite any existing commit.
|
|
@@ -40,22 +40,37 @@ Plan affected spec updates before, with, or after their corresponding code chang
|
|
|
40
40
|
For an existing IR, read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
41
41
|
Do not implement a later task in this phase.
|
|
42
42
|
Mark the IR's progress and deliverables when relevant.
|
|
43
|
+
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.
|
|
44
|
+
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.
|
|
45
|
+
Report only what the IR and completed work establish, in ordinary prose without a required marker.
|
|
43
46
|
If the IR will be finished after this phase, double-check that all acceptance criteria are met.
|
|
44
47
|
|
|
45
48
|
Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
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.
|
|
52
|
+
CODE scope commit and evaluated revision remain distinct because REVIEW may create fix commits.
|
|
53
|
+
No caller option, player prose or repository status text supplies this record.
|
|
54
|
+
The record survives permitted restoration and the current task's Boss-answer continuation, and a fresh run or explicit Boss interruption clears it.
|
|
55
|
+
|
|
56
|
+
At the start of every later IR-task phase, Captain shall relay to Coder the original caller input, the IR identity, any relevant run results, and that previous phase review as quoted evidence in quotes (`>`), along with the following instruction; when absent, the record reads "No accepted prior-phase review is available.":
|
|
49
57
|
|
|
50
58
|
> Original request: <caller-input>
|
|
51
59
|
> IR number: <ir-number>
|
|
52
60
|
> Run results: <run-results>
|
|
61
|
+
> Previous phase review: <previous-phase-review>
|
|
53
62
|
|
|
54
63
|
```markdown
|
|
55
64
|
Read the identified IR and implement exactly its next unfinished task, including corresponding tests or specs if any.
|
|
56
65
|
Do not implement a later task in this phase.
|
|
57
66
|
Mark the IR's progress and deliverables when relevant.
|
|
67
|
+
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.
|
|
68
|
+
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.
|
|
69
|
+
Report only what the IR and completed work establish, in ordinary prose without a required marker.
|
|
58
70
|
If the IR will be finished after this phase, double-check that all acceptance criteria are met.
|
|
71
|
+
Use the quoted previous-phase review only for its exact recorded scope; committed pending-review text may predate that canonical result.
|
|
72
|
+
Before treating it as current review evidence, verify clean current HEAD equals its evaluatedRevision; a mismatch is not approval.
|
|
73
|
+
It does not replace independent review of this task or any new owner or release decision.
|
|
59
74
|
```
|
|
60
75
|
|
|
61
76
|
At the start of *every* phase, Captain shall append the following instruction:
|
|
@@ -63,7 +78,10 @@ At the start of *every* phase, Captain shall append the following instruction:
|
|
|
63
78
|
```markdown
|
|
64
79
|
Keep to the original intent and follow what it asks.
|
|
65
80
|
Do not re-run tests or builds whose inputs have not changed since any previous reported run.
|
|
66
|
-
|
|
81
|
+
If a required Boss answer prevents completion of the current phase or IR task, ask that question and return without committing.
|
|
82
|
+
Preserve permitted uncommitted work for the deferred continuation; do not create a partial or blocker-only commit to meet the one-commit requirement.
|
|
83
|
+
After the answer allows completion, make exactly one phase-owned commit before REVIEW.
|
|
84
|
+
When the phase is complete, make its minimal changes and then one new commit, following @specs/packages/git.md; never amend an existing commit.
|
|
67
85
|
Make the commit message explain concisely what changed and why, including relevant verification.
|
|
68
86
|
Identify every new commit you make.
|
|
69
87
|
Credit every AI that contributed to this commit: Coder <coder-llm>.
|
|
@@ -73,9 +91,11 @@ Every new-intent phase has two semantic outcomes: direct implementation and new
|
|
|
73
91
|
Every IR-task phase, including the first phase for an existing IR, identifies the IR and the implemented task, and has two semantic outcomes: more tasks and final task.
|
|
74
92
|
The first phase therefore has four semantic outcomes — direct implementation, new IR, more tasks, and final task — plus the Boss question when the continued IR is ambiguous.
|
|
75
93
|
Each semantic outcome requires affirmative support in Coder's result, but no phase transition shall depend on a fixed presentation format of Coder's reply.
|
|
94
|
+
The Boss-question outcome means that, 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.
|
|
95
|
+
A commit statement alone does not establish semantic completion, and contradictory or insufficient evidence supports no forced commit outcome.
|
|
76
96
|
Captain shall use the repository-effect receipt as the authoritative identity of the phase's new commit.
|
|
77
97
|
|
|
78
|
-
At the end of every phase, Captain shall call playbook `review` and input the following in quotes (`>`):
|
|
98
|
+
At the end of every completed phase, Captain shall call playbook `review` and input the following in quotes (`>`):
|
|
79
99
|
|
|
80
100
|
> Original intent: \<caller-input\>
|
|
81
101
|
> Review scope: the commit \<code-commit\> from this coding phase and its resulting repository state.
|
|
@@ -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;
|
|
@@ -446,12 +446,27 @@ export interface PlaybookCaptainShell extends Captain {
|
|
|
446
446
|
// normalized `captain.options.playbooks.<id>` role map.
|
|
447
447
|
interface Enablement {
|
|
448
448
|
entry: PlaybookCaptainRegistryEntry;
|
|
449
|
+
readonly configuredFrom: string;
|
|
449
450
|
artifactSchema: 3;
|
|
450
451
|
command: string;
|
|
451
452
|
options: JsonValue;
|
|
452
453
|
roleBindings: ReadonlyMap<string, EffectivePlayerBinding>;
|
|
453
454
|
}
|
|
454
455
|
|
|
456
|
+
function missingPackagedReview(
|
|
457
|
+
enablementById: ReadonlyMap<string, Enablement>,
|
|
458
|
+
playbookId: string,
|
|
459
|
+
): string | undefined {
|
|
460
|
+
const selected = enablementById.get(playbookId);
|
|
461
|
+
if (
|
|
462
|
+
selected === undefined ||
|
|
463
|
+
enablementById.has('review') ||
|
|
464
|
+
!['code', 'decide'].includes(playbookId) ||
|
|
465
|
+
selected.configuredFrom !== `@sublang/playbook/${playbookId}/registry`
|
|
466
|
+
) return undefined;
|
|
467
|
+
return `/${selected.command} requires enabled playbook "review"; enable REVIEW before starting this packaged workflow`;
|
|
468
|
+
}
|
|
469
|
+
|
|
455
470
|
function createRuntimeForEnablement(
|
|
456
471
|
enablement: Enablement,
|
|
457
472
|
hostCapabilitiesById: ReadonlyMap<
|
|
@@ -4181,6 +4196,7 @@ async function buildEnablements(
|
|
|
4181
4196
|
hostCapabilitiesById.set(entry.id, hostCapability);
|
|
4182
4197
|
enablementById.set(entry.id, {
|
|
4183
4198
|
entry,
|
|
4199
|
+
configuredFrom: from,
|
|
4184
4200
|
artifactSchema,
|
|
4185
4201
|
command,
|
|
4186
4202
|
options: validatedOptions,
|
|
@@ -6142,13 +6158,16 @@ export function createPlaybookCaptainShell(
|
|
|
6142
6158
|
enablement,
|
|
6143
6159
|
hostCapabilitiesById,
|
|
6144
6160
|
);
|
|
6161
|
+
// A nested child inherits only its parent's selection, never the turn's
|
|
6162
|
+
// unselected references; only a root reads the turn's selection.
|
|
6163
|
+
const inherited = parent ? parent.frame.attachments : (activeTurn?.selectedAttachments ?? activeTurn?.attachments);
|
|
6145
6164
|
return {
|
|
6146
6165
|
entry,
|
|
6147
6166
|
enablement,
|
|
6148
6167
|
runtime,
|
|
6149
6168
|
sessionId,
|
|
6150
6169
|
rootSessionId: parent?.frame.rootSessionId ?? sessionId,
|
|
6151
|
-
...(
|
|
6170
|
+
...(inherited?.length ? { attachments: [...inherited] } : {}),
|
|
6152
6171
|
depth: parent ? parent.frame.depth + 1 : 0,
|
|
6153
6172
|
playerBindings,
|
|
6154
6173
|
...(parent ? { parent } : {}),
|
|
@@ -6486,7 +6505,6 @@ export function createPlaybookCaptainShell(
|
|
|
6486
6505
|
} finally {
|
|
6487
6506
|
if (leafFrame() === frame) {
|
|
6488
6507
|
frames.pop();
|
|
6489
|
-
if (!frame.parent) pendingAttachments = [];
|
|
6490
6508
|
if (frame.parent) {
|
|
6491
6509
|
pendingChildParents.delete(frame.parent.frame);
|
|
6492
6510
|
}
|
|
@@ -6930,6 +6948,23 @@ export function createPlaybookCaptainShell(
|
|
|
6930
6948
|
return;
|
|
6931
6949
|
}
|
|
6932
6950
|
assertRetainableResult(frame, result);
|
|
6951
|
+
// A deferred checkpoint refusal changes the runtime's control overlay
|
|
6952
|
+
// without publishing an FSM transition. Refresh its derived question
|
|
6953
|
+
// mirror from an exact safe snapshot before reporting or saving the stop.
|
|
6954
|
+
if (leafFrame() === frame && !frame.disposing &&
|
|
6955
|
+
retainedEffectReconciliation === undefined) {
|
|
6956
|
+
try {
|
|
6957
|
+
const exported = frame.runtime.exportSnapshot?.();
|
|
6958
|
+
if (exported !== undefined) {
|
|
6959
|
+
const snapshot = assertPlaybookRuntimeSnapshot(exported, frame.entry.id);
|
|
6960
|
+
if (isDeepStrictEqual(snapshot.state, result.state) &&
|
|
6961
|
+
isDeepStrictEqual(snapshot.effectLedger, currentEffectLedger())) {
|
|
6962
|
+
pendingBossQuestions = snapshot.pendingBossQuestions.length === 0 && pendingBossQuestions === undefined
|
|
6963
|
+
? undefined : mirroredBossQuestions(snapshot.pendingBossQuestions);
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
} catch { /* An inconsistent runtime must still fail exact capture. */ }
|
|
6967
|
+
}
|
|
6933
6968
|
if (result.outcome === 'aborted' && runFailureFacts) {
|
|
6934
6969
|
runFailureFacts.push(
|
|
6935
6970
|
`${frameLabel(frame)} was aborted before its outcome could be confirmed; it was not repeated automatically.`,
|
|
@@ -7011,6 +7046,9 @@ export function createPlaybookCaptainShell(
|
|
|
7011
7046
|
);
|
|
7012
7047
|
}
|
|
7013
7048
|
|
|
7049
|
+
const reviewRefusal = missingPackagedReview(enablementById, entry.id);
|
|
7050
|
+
if (reviewRefusal !== undefined) throw new Error(reviewRefusal);
|
|
7051
|
+
|
|
7014
7052
|
pendingChildParents.add(parent);
|
|
7015
7053
|
let child: EngagementFrame;
|
|
7016
7054
|
try {
|
|
@@ -9004,7 +9042,13 @@ export function createPlaybookCaptainShell(
|
|
|
9004
9042
|
// its decision-call prose crosses the presentation boundary. Acting
|
|
9005
9043
|
// selections freeze after their work and before reporting begins.
|
|
9006
9044
|
if (selection.action === 'respond') freezeControllerEvidence();
|
|
9007
|
-
|
|
9045
|
+
signal.throwIfAborted();
|
|
9046
|
+
const reviewRefusal = selection.action === 'start' || selection.action === 'switch'
|
|
9047
|
+
? missingPackagedReview(enablementById, selection.playbookId)
|
|
9048
|
+
: undefined;
|
|
9049
|
+
let settlement = reviewRefusal === undefined
|
|
9050
|
+
? await executeSelection(selection, signal, automaticRecovery)
|
|
9051
|
+
: await rejectSelection(selection, reviewRefusal);
|
|
9008
9052
|
// One controller action can include bounded prerequisite recovery.
|
|
9009
9053
|
// Neither the model nor a retry manufactures an unadvertised transition.
|
|
9010
9054
|
if (turn && abortPreparation && settlement.status !== 'rejected' && !['respond', 'dismiss', 'recover'].includes(selection.action)) {
|
|
@@ -9548,6 +9592,9 @@ export function createPlaybookCaptainShell(
|
|
|
9548
9592
|
const selected = selection.attachmentIds === undefined ? turn.attachments : selection.attachmentIds.map((id) => available.get(id as SessionAssetRef['assetId'])!);
|
|
9549
9593
|
const nextAttachments = mergeAttachments(leaf.attachments ?? [], selected);
|
|
9550
9594
|
if (nextAttachments.length) leaf.attachments = nextAttachments;
|
|
9595
|
+
// Delivered references now belong to the leaf; the rest stay pending.
|
|
9596
|
+
const delivered = new Set(selected.map((reference) => reference.assetId));
|
|
9597
|
+
pendingAttachments = pendingAttachments.filter((reference) => !delivered.has(reference.assetId));
|
|
9551
9598
|
// CAPTAIN-8: delivery carries text only, and the shell is authoritative
|
|
9552
9599
|
// for that text — any text carried on the selection is ignored.
|
|
9553
9600
|
turn.settled = true;
|
|
@@ -102,9 +102,11 @@ export function createAssetStore(options) {
|
|
|
102
102
|
try {
|
|
103
103
|
const opened = await directoryHandle.stat();
|
|
104
104
|
if (opened.dev !== before.dev || opened.ino !== before.ino) throw new Error('asset owner directory changed during preparation');
|
|
105
|
-
|
|
105
|
+
// fchmod always moves ctime, which verified readers pin: change only modes that differ.
|
|
106
|
+
if (process.platform !== 'win32' && (opened.mode & 0o777) !== 0o700) await directoryHandle.chmod(0o700);
|
|
106
107
|
for (const name of await readdir(directory)) {
|
|
107
|
-
|
|
108
|
+
// Foreign names (Finder or editor files) are neither assets nor ours to modify.
|
|
109
|
+
if (!/^(?:[0-9a-f]{64}(?:\.json)?|\.import-[0-9a-f-]+\.tmp)$/.test(name)) continue;
|
|
108
110
|
const path = join(directory, name);
|
|
109
111
|
const stat = await lstat(path);
|
|
110
112
|
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) throw new Error('asset entry must be a single-link regular file');
|
|
@@ -114,7 +116,7 @@ export function createAssetStore(options) {
|
|
|
114
116
|
try {
|
|
115
117
|
const pinned = await handle.stat();
|
|
116
118
|
if (!sameFile(stat, pinned)) throw new Error('asset entry changed during preparation');
|
|
117
|
-
if (process.platform !== 'win32') await handle.chmod(0o600);
|
|
119
|
+
if (process.platform !== 'win32' && (pinned.mode & 0o777) !== 0o600) await handle.chmod(0o600);
|
|
118
120
|
assertFile(await handle.stat(), 'asset entry');
|
|
119
121
|
const final = await lstat(path);
|
|
120
122
|
if (final.dev !== pinned.dev || final.ino !== pinned.ino || final.nlink !== 1) throw new Error('asset entry changed during preparation');
|
|
@@ -286,9 +288,8 @@ export function createAssetStore(options) {
|
|
|
286
288
|
const entries = await readdir(directory);
|
|
287
289
|
const result = [];
|
|
288
290
|
for (const name of entries.sort()) {
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
if (!/^[0-9a-f]{64}$/.test(name)) throw new Error(`unknown asset owner entry ${JSON.stringify(basename(name))}`);
|
|
291
|
+
// Descriptors, import temporaries and foreign files are not enumerated.
|
|
292
|
+
if (!/^[0-9a-f]{64}$/.test(name)) continue;
|
|
292
293
|
if (!entries.includes(`${name}.json`)) { const orphan = await checkedOpen(name, 'unreferenced asset content'); await orphan.handle.close(); continue; }
|
|
293
294
|
const reader = await openAsset(`sha256:${name}`); await reader.close();
|
|
294
295
|
result.push(Object.freeze({ assetId: reader.reference.assetId, path: name, metadataPath: `${name}.json`, byteLength: reader.reference.byteLength }));
|
package/slc/gears2fsm.md
CHANGED
|
@@ -602,6 +602,9 @@ safety beyond the guarded direct transition.
|
|
|
602
602
|
## Context and prompts
|
|
603
603
|
|
|
604
604
|
Context fields used to drive guards or compose prompts shall be **typed and named**.
|
|
605
|
+
Where Source owns array-valued retained discussion whose initial state is no history, initialize that context field to `[]` and reset it to `[]` only at its Source lifecycle boundaries.
|
|
606
|
+
Carry its declared array shape through ordinary and continuation `invoke.input`; do not leave the empty structural history absent so a prompt-contract probe mistakes it for a scalar.
|
|
607
|
+
This empty history is not a default caller intent, prerequisite report, domain evidence, approval, or prior-stage result; those remain unavailable until actually established.
|
|
605
608
|
The compiler shall not branch on untyped properties of `lastResult`; persistent routing decisions belong in typed context fields. (`lastResult` is for inspection only.)
|
|
606
609
|
Where Source declares a finite ordered plan, represent it as a typed readonly
|
|
607
610
|
JSON-safe array and validate that shape on the actor-output transition; an
|
|
@@ -835,8 +838,8 @@ Only
|
|
|
835
838
|
The canonical storage paths are `context.pendingBossQuestion` and `context.bossReply` for the scalar form, or `context.pendingBossQuestions[stateId]` and `context.bossReplies[stateId]` for the keyed form.
|
|
836
839
|
A private wrapper such as `context.continuation` shall not replace these fields directly on machine context.
|
|
837
840
|
|
|
838
|
-
A machine
|
|
839
|
-
|
|
841
|
+
A machine that declares no root parallel group shall use the scalar form;
|
|
842
|
+
keyed storage alone is not visible to the flat shared factory:
|
|
840
843
|
|
|
841
844
|
- An `awaitBossReply` state with stable `id: 'awaitBossReply'`, tag
|
|
842
845
|
`playbook.parked`, and description
|
|
@@ -847,7 +850,9 @@ form:
|
|
|
847
850
|
- `resumableStates(ids)`, `setPendingBossQuestion`, and
|
|
848
851
|
`clearBossReplyContext` helpers with the existing single-question behavior.
|
|
849
852
|
|
|
850
|
-
A machine
|
|
853
|
+
A machine that declares a root parallel group shall use the keyed form
|
|
854
|
+
throughout the machine, including any sequential working leaves and their
|
|
855
|
+
root reply wait:
|
|
851
856
|
|
|
852
857
|
- One local waiting leaf per branch, tagged `playbook.parked`.
|
|
853
858
|
- A `BOSS_REPLY` event carrying `{ questionId: string; answer: string }`.
|
|
@@ -884,6 +889,15 @@ pending question and reply selected for that working leaf as singular
|
|
|
884
889
|
`pendingBossQuestion` and `bossReply` fields, regardless of the scalar or keyed
|
|
885
890
|
context representation, so prompt composition has one stable contract.
|
|
886
891
|
When Source requires earlier discussion or constraints on a later invocation, the compiler shall persist that source-owned history in serializable machine context before replacing or clearing the pending Q/A fields.
|
|
892
|
+
Where that Source-owned value is the complete received Boss reply, retain it
|
|
893
|
+
directly from the current working leaf's canonical reply context at the
|
|
894
|
+
Source-authored accepted-outcome boundary, before clearing that leaf's Q/A.
|
|
895
|
+
Preserve its exact string, including surrounding whitespace, CRLF/LF separators,
|
|
896
|
+
blank lines and trailing separators; do not require a model-produced echo or
|
|
897
|
+
replace it with one. Acceptance remains the acting agent's semantic judgment,
|
|
898
|
+
guarded by the current matching question's state, Source-item and asker binding
|
|
899
|
+
and actual receipt of a nonempty reply. Neither stale question/reply context
|
|
900
|
+
nor a different actor-supplied value establishes that binding or approval.
|
|
887
901
|
The later `invoke.input` shall relay that persisted context on both resumed and fresh calls that Source says need it, preserving Source-owned relevance and format without imposing all-history semantics on sources that do not require it.
|
|
888
902
|
Shared Boss-reply continuation carries only the latest Q/A pair, and a backend continuation token is not durable Source history.
|
|
889
903
|
When both fields are present, the linked runtime shall compose the continuation preamble and labelled Q&A blocks per [link.md "Player prompt composition"](link.md#player-prompt-composition).
|
package/slc/text2gears.md
CHANGED
|
@@ -189,6 +189,13 @@ between the Source and the catalog: leave the Target unwritten and report it
|
|
|
189
189
|
as an incompatible compiler input rather than rename or invent.
|
|
190
190
|
A Source outside those ids is named from its own words as above.
|
|
191
191
|
|
|
192
|
+
A value Source assigns to the received Boss input or reply is already runtime
|
|
193
|
+
input, not a value the acting agent produces. Where Source requires that
|
|
194
|
+
complete value retained for later calls, preserve the exact input's retention
|
|
195
|
+
and the Source's acceptance condition in non-acting GEARS prose; do not declare
|
|
196
|
+
a Judge-extracted echo property for it. The semantic approval or refusal stays
|
|
197
|
+
in the acting result contract.
|
|
198
|
+
|
|
192
199
|
A produced value consumed later shall have a declared producer: where any
|
|
193
200
|
later item's blockquote reads a value through a `<placeholder>`, the item
|
|
194
201
|
whose behavior produces that value shall declare the `Results:` contract
|
|
@@ -866,12 +866,16 @@ export function defaultExtractRequiredFields(description) {
|
|
|
866
866
|
fields.push(m[1]);
|
|
867
867
|
return fields;
|
|
868
868
|
}
|
|
869
|
+
const CURRENT_CALL_ADJUDICATION_GUIDANCE = "Classify the invoked role's current call by the result it reports at call end, using the full output and declared outcome meanings. " +
|
|
870
|
+
'An earlier interim question is not outstanding when later prose explicitly resolves or supersedes it for this call; a prerequisite only for a later workflow stage does not block an affirmatively completed current call. ' +
|
|
871
|
+
'A genuinely unanswered question needed to finish the current call remains outstanding. A commit statement alone does not establish semantic completion; do not force contradictory or insufficient evidence into a completed outcome.';
|
|
869
872
|
/** Default delegated-player adjudicator prompt. */
|
|
870
873
|
export function defaultBuildJudgePrompt(input, finalText) {
|
|
871
874
|
const lines = [];
|
|
872
875
|
lines.push('This is hidden control work. Do not call tools, inspect files, or ' +
|
|
873
876
|
'seek external evidence. Decide only from the supplied player output ' +
|
|
874
877
|
'and outcome descriptions. Reply with exactly one JSON object and no prose.');
|
|
878
|
+
lines.push(CURRENT_CALL_ADJUDICATION_GUIDANCE);
|
|
875
879
|
lines.push('');
|
|
876
880
|
lines.push(`The ${input.role} role just produced this output:`);
|
|
877
881
|
lines.push('');
|
|
@@ -957,6 +961,7 @@ function buildGovernedJudgePrompt(input, finalText, outcomes, correction) {
|
|
|
957
961
|
const lines = [
|
|
958
962
|
'This is hidden control work. Do not call tools, inspect files, or seek external evidence.',
|
|
959
963
|
'Decide only from the supplied player output and declared outcomes.',
|
|
964
|
+
CURRENT_CALL_ADJUDICATION_GUIDANCE,
|
|
960
965
|
'Reply with exactly one JSON object and no prose.',
|
|
961
966
|
'',
|
|
962
967
|
`The ${input.role} role just produced this output:`,
|
|
@@ -1706,6 +1706,11 @@ export function defaultExtractRequiredFields(description: string): string[] {
|
|
|
1706
1706
|
return fields;
|
|
1707
1707
|
}
|
|
1708
1708
|
|
|
1709
|
+
const CURRENT_CALL_ADJUDICATION_GUIDANCE =
|
|
1710
|
+
"Classify the invoked role's current call by the result it reports at call end, using the full output and declared outcome meanings. " +
|
|
1711
|
+
'An earlier interim question is not outstanding when later prose explicitly resolves or supersedes it for this call; a prerequisite only for a later workflow stage does not block an affirmatively completed current call. ' +
|
|
1712
|
+
'A genuinely unanswered question needed to finish the current call remains outstanding. A commit statement alone does not establish semantic completion; do not force contradictory or insufficient evidence into a completed outcome.';
|
|
1713
|
+
|
|
1709
1714
|
/** Default delegated-player adjudicator prompt. */
|
|
1710
1715
|
export function defaultBuildJudgePrompt(
|
|
1711
1716
|
input: PlaybookPlayerInput,
|
|
@@ -1717,6 +1722,7 @@ export function defaultBuildJudgePrompt(
|
|
|
1717
1722
|
'seek external evidence. Decide only from the supplied player output ' +
|
|
1718
1723
|
'and outcome descriptions. Reply with exactly one JSON object and no prose.',
|
|
1719
1724
|
);
|
|
1725
|
+
lines.push(CURRENT_CALL_ADJUDICATION_GUIDANCE);
|
|
1720
1726
|
lines.push('');
|
|
1721
1727
|
lines.push(`The ${input.role} role just produced this output:`);
|
|
1722
1728
|
lines.push('');
|
|
@@ -1822,6 +1828,7 @@ function buildGovernedJudgePrompt(
|
|
|
1822
1828
|
const lines = [
|
|
1823
1829
|
'This is hidden control work. Do not call tools, inspect files, or seek external evidence.',
|
|
1824
1830
|
'Decide only from the supplied player output and declared outcomes.',
|
|
1831
|
+
CURRENT_CALL_ADJUDICATION_GUIDANCE,
|
|
1825
1832
|
'Reply with exactly one JSON object and no prose.',
|
|
1826
1833
|
'',
|
|
1827
1834
|
`The ${input.role} role just produced this output:`,
|