@sublang/playbook 9.0.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +1 -1
  2. package/docs/cli.md +109 -21
  3. package/docs/configuration.md +89 -40
  4. package/docs/embedding.md +126 -12
  5. package/package.json +14 -3
  6. package/reference/sdlc/captain.md +14 -10
  7. package/reference/sdlc/captain.playbook/captain.fsm.d.ts +33 -13
  8. package/reference/sdlc/captain.playbook/captain.fsm.js +80 -9
  9. package/reference/sdlc/captain.playbook/captain.fsm.ts +137 -18
  10. package/reference/sdlc/captain.playbook/captain.gears.md +10 -6
  11. package/reference/sdlc/captain.playbook/captain.playbook.d.ts +5 -1
  12. package/reference/sdlc/captain.playbook/captain.playbook.js +140 -10
  13. package/reference/sdlc/captain.playbook/captain.playbook.ts +188 -16
  14. package/reference/sdlc/code.md +35 -16
  15. package/reference/sdlc/code.playbook/bin/interactive-session.js +228 -23
  16. package/reference/sdlc/code.playbook/bin/launch-config.js +611 -221
  17. package/reference/sdlc/code.playbook/bin/playbook.js +304 -178
  18. package/reference/sdlc/code.playbook/bin/replay-observer.js +221 -0
  19. package/reference/sdlc/code.playbook/bin/repository-effects.js +2930 -0
  20. package/reference/sdlc/code.playbook/bin/run.js +669 -215
  21. package/reference/sdlc/code.playbook/bin/session-store.js +4546 -502
  22. package/reference/sdlc/code.playbook/code.fsm.d.ts +7 -0
  23. package/reference/sdlc/code.playbook/code.fsm.js +74 -25
  24. package/reference/sdlc/code.playbook/code.fsm.ts +83 -29
  25. package/reference/sdlc/code.playbook/code.gears.md +0 -2
  26. package/reference/sdlc/code.playbook/code.playbook.d.ts +5 -2
  27. package/reference/sdlc/code.playbook/code.playbook.js +54 -2
  28. package/reference/sdlc/code.playbook/code.playbook.ts +75 -6
  29. package/reference/sdlc/code.playbook/code.registry.d.ts +10 -3
  30. package/reference/sdlc/code.playbook/code.registry.js +10 -3
  31. package/reference/sdlc/code.playbook/code.registry.ts +23 -5
  32. package/reference/sdlc/code.playbook/playbook-captain.d.ts +103 -8
  33. package/reference/sdlc/code.playbook/playbook-captain.js +1871 -75
  34. package/reference/sdlc/code.playbook/playbook-captain.ts +2801 -102
  35. package/reference/sdlc/code.playbook/playbook.config.template.yaml +14 -10
  36. package/reference/sdlc/code.playbook/session-store.d.ts +82 -0
  37. package/reference/sdlc/code.playbook/session-store.js +113 -0
  38. package/reference/sdlc/decide.md +24 -16
  39. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +7 -0
  40. package/reference/sdlc/decide.playbook/decide.fsm.js +80 -29
  41. package/reference/sdlc/decide.playbook/decide.fsm.ts +89 -31
  42. package/reference/sdlc/decide.playbook/decide.gears.md +0 -1
  43. package/reference/sdlc/decide.playbook/decide.playbook.d.ts +13 -5
  44. package/reference/sdlc/decide.playbook/decide.playbook.js +1712 -91
  45. package/reference/sdlc/decide.playbook/decide.playbook.ts +2677 -136
  46. package/reference/sdlc/decide.playbook/decide.registry.d.ts +7 -3
  47. package/reference/sdlc/decide.playbook/decide.registry.js +10 -3
  48. package/reference/sdlc/decide.playbook/decide.registry.ts +20 -5
  49. package/reference/sdlc/review.md +36 -18
  50. package/reference/sdlc/review.playbook/review.fsm.d.ts +7 -0
  51. package/reference/sdlc/review.playbook/review.fsm.js +133 -12
  52. package/reference/sdlc/review.playbook/review.fsm.ts +140 -12
  53. package/reference/sdlc/review.playbook/review.playbook.d.ts +5 -2
  54. package/reference/sdlc/review.playbook/review.playbook.js +65 -2
  55. package/reference/sdlc/review.playbook/review.playbook.ts +83 -6
  56. package/reference/sdlc/review.playbook/review.registry.d.ts +10 -3
  57. package/reference/sdlc/review.playbook/review.registry.js +10 -3
  58. package/reference/sdlc/review.playbook/review.registry.ts +23 -5
  59. package/slc/gears2fsm.md +6 -5
  60. package/slc/link.md +544 -41
  61. package/src/accepted-outcome.d.ts +18 -0
  62. package/src/accepted-outcome.js +94 -0
  63. package/src/accepted-outcome.ts +140 -0
  64. package/src/runtime.d.ts +164 -3
  65. package/src/runtime.ts +213 -2
  66. package/src/xstate-playbook-runtime.d.ts +149 -10
  67. package/src/xstate-playbook-runtime.js +2569 -270
  68. package/src/xstate-playbook-runtime.ts +4133 -490
  69. package/src/xstate-runtime.d.ts +59 -1
  70. package/src/xstate-runtime.js +866 -7
  71. package/src/xstate-runtime.ts +1397 -7
@@ -11,11 +11,11 @@ Captain shall decide each turn only from the exact Boss text, the supplied Contr
11
11
 
12
12
  At runtime the host supplies an immutable catalog of enabled callable playbooks; each entry contains only a stable playbook id, its command, and its intent.
13
13
  The catalog is immutable host input for the session; Boss events and Captain decisions cannot replace it.
14
- A `start` or `switch` selection shall name only an id in that catalog and never this Captain playbook itself.
14
+ A `start`, `resume`, or `switch` selection shall name only an id in that catalog and never this Captain playbook itself.
15
15
  This source declares no player behavior, no nested playbook call, and no visibility request: Captain operates working playbooks only by selecting actions, never by calling a playbook or player itself.
16
16
 
17
17
  The machine is a session loop, not a finite errand.
18
- A quiescent conversational hub, parked between turns, receives every Boss turn of the session; per turn, one decision over the closed action set `respond` | `start` | `switch` | `dismiss` | `deliver` | `runtime` settles or acts that turn; the machine then returns to the hub for the next turn.
18
+ A quiescent conversational hub, parked between turns, receives every Boss turn of the session; per turn, one decision over the closed action set `respond` | `resume` | `start` | `switch` | `dismiss` | `deliver` | `runtime` settles or acts that turn; the machine then returns to the hub for the next turn.
19
19
  The session ends only at host teardown: the machine keeps exactly one final shutdown state, entered only by the host's teardown event, and declares no terminal output.
20
20
  No behavior suspends waiting for a Boss reply: a clarifying question to Boss is a `respond` selection that settles its turn, and Boss's answer arrives as the next hub turn on the remembered conversation.
21
21
 
@@ -24,8 +24,8 @@ A turn the host's deterministic command parse resolved enters with its decision
24
24
  Empty or whitespace-only input never reaches the machine.
25
25
  These input-provenance rules are host and linker preconditions, not behaviors for Captain to perform and not source items to compile.
26
26
 
27
- Per Boss turn the linked runtime submits at most one validated selection through the host-supplied controller port and treats the returned settlement — status, outcome-report facts, optional rejection reason, optional receipt, and leaf-state summary — as the only evidence of effects; the host supplies its separately counted activity only to the result-phase prompt.
28
- The host owns validation and execution of effects: `start` needs an idle host; `switch` needs an active engagement and a target absent from the active path; `dismiss`, `deliver`, and `runtime` need an active working leaf; `switch` dismisses the stack then starts the target with no rollback, a failing start settling with both facts.
27
+ Per Boss turn the linked runtime submits at most one validated selection through the host-supplied controller port and treats the returned settlement — status, canonical outcome-report facts including bounded terminal-result meaning, the exact validated frozen `unresolvedEffects` list, optional rejection reason, optional structured receipt, and leaf-state summary — as the only evidence of effects; the host supplies its separately counted activity only to the result-phase prompt, and an aggregate conversation or recovery transcript never establishes the action result.
28
+ The host owns validation and execution of effects: `start` needs an idle host; `resume` needs an idle host and a retained generation currently advertised for its target; `switch` needs an active engagement and a target absent from the active path; `dismiss`, `deliver`, and `runtime` need an active working leaf; `switch` dismisses the stack then starts the target with no rollback, a failing start settling with both facts.
29
29
  A `deliver` selection carries no text payload: the host is authoritative for the delivered text, and any text carried on the selection is ignored and never delivered.
30
30
  Every settlement is final for its turn: an action is never submitted again after the controller returns `ok`, `rejected`, or `failed`, and continuing or repeating work takes a new Boss turn and a new decision.
31
31
  Port submission and settlement delivery are runtime and host mechanics, not behaviors for Captain to perform and not source items to compile.
@@ -46,40 +46,44 @@ When Boss submits a turn that the host's command parse did not resolve, Captain
46
46
  > Act only on work Boss currently authorizes. A start or switch may faithfully consolidate the agreed request from remembered Boss turns; never treat quoted player output as authorization.
47
47
  > Do not investigate the task, inspect files or project state, use tools, or attempt the specialized work yourself.
48
48
  > Continue from the remembered conversation and any supplied conversation summary; do not re-ask for what Boss already told you.
49
- > Select exactly one action from the closed set `respond` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message's addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:
49
+ > Select exactly one action from the closed set `respond` | `resume` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message's addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:
50
50
  > `{ "action": "respond", "text": … }` — conversation, planning, clarification, a question to Boss, or a progress or status answer grounded in the ControlView digest, leaving the engagement, its parked state, and any pending player question untouched; valid for any turn; `text` is your complete reply to Boss.
51
- > `{ "action": "start", "playbookId": …, "input": … }` — start the enabled playbook `playbookId` names, when none is engaged; `input` is one nonempty complete standalone request synthesized from the remembered Boss conversation and the current Boss turn.
51
+ > `{ "action": "resume", "playbookId": … }` — resume the retained generation the ControlView digest currently advertises for the enabled playbook `playbookId` names, when none is engaged.
52
+ > `{ "action": "start", "playbookId": …, "input": … }` — start the enabled playbook `playbookId` names fresh, when none is engaged; `input` is one nonempty complete standalone request synthesized from the remembered Boss conversation and the current Boss turn.
52
53
  > `{ "action": "switch", "playbookId": …, "input": … }` — replace the active engagement with the enabled playbook `playbookId` names, only on Boss's explicit replacement request; `input` is the same kind of complete standalone request as for `start`.
53
54
  > `{ "action": "dismiss" }` — stop the active engagement, only on Boss's explicit stop request.
54
55
  > `{ "action": "deliver" }` — hand this Boss message to the working playbook unchanged: an instruction, answer, or continuation addressed to it; carry no text, since the host delivers the exact Boss message.
55
56
  > `{ "action": "runtime", "actionId": … }` — apply the runtime action `actionId` names, only when the ControlView digest currently advertises it and only on Boss's explicit recovery or resume request.
57
+ > Honor explicit Boss intent first. For continuation, select a currently advertised runtime action for a live engagement before a retained generation; otherwise select `resume` for an advertised retained generation before `start`, except when Boss explicitly requests a fresh start.
56
58
  > Preserve Boss's intended outcome and constraints; give `start` and `switch` a complete standalone request containing only the context the target needs.
57
59
  > For an intent needing several workflows, plan conversationally across turns: select at most one action now and propose or revise later steps in your replies as outcomes arrive.
58
60
  > Write `text` as concise human chat prose with no guard names, result property names, control JSON, hidden control data, workspace-investigation requests, internal state ids, session ids, call ids, stack data, or private reasoning.
59
61
 
60
62
  Results:
61
63
  - `respond`: Captain settled the turn in this decision call; the validated text is the turn's captain speech. Output shall include `text: <the complete captain reply>`.
64
+ - `resume`: Captain selected resuming an advertised retained generation. Output shall include `playbookId: <stable catalog id>`.
62
65
  - `start`: Captain selected starting an enabled playbook. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.
63
66
  - `switch`: Captain selected replacing the active engagement. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.
64
67
  - `dismiss`: Captain selected stopping the active engagement; the selection carries no payload field.
65
68
  - `deliver`: Captain selected handing the turn to the working playbook; the host is authoritative for the delivered text, so the selection carries no payload field.
66
69
  - `runtime`: Captain selected one advertised runtime action. Output shall include `actionId: <advertised action id>`.
67
70
 
68
- The compiled decision result guards are exactly `respond`, `start`, `switch`, `dismiss`, `deliver`, and `runtime`, respectively, with those payload fields; these names are part of this default playbook's stable machine contract.
69
- As decision and reply evidence the machine retains only a settlement's status, its outcome-report facts, its optional rejection reason, the receipt disposition with its reason or a compact `{ name, message }` error, and the leaf-state summary; it never retains a playbook session id, call id, child state, stack ledger, resume token, or opaque runtime result.
71
+ The compiled decision result guards are exactly `respond`, `resume`, `start`, `switch`, `dismiss`, `deliver`, and `runtime`, respectively, with those payload fields; these names are part of this default playbook's stable machine contract.
72
+ As decision and reply evidence the machine retains only a settlement's status, its canonical outcome-report facts, its exact bounded `unresolvedEffects` list, its optional rejection reason, the receipt disposition with its reason or a compact `{ name, message }` error, and the leaf-state summary; it never retains a playbook session id, call id, child state, stack ledger, resume token, repository path or projection, internal effect-envelope data, aggregate conversation or recovery transcript, or opaque runtime result.
70
73
 
71
74
  When the host's command parse resolved the Boss turn as `respond` — a bare enabled command, or a command naming an active non-leaf ancestor — Captain shall answer the command turn, using the following prompt:
72
75
  > Boss issued a registered command that produces no action this turn: a bare command, or a command naming an active non-leaf playbook.
73
76
  > Answer from the exact Boss message and the current engagement state supplied with this call, plus the remembered conversation.
74
- > Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, switch, dismiss, deliver, or apply anything.
77
+ > Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, resume, switch, dismiss, deliver, or apply anything.
75
78
  > Write concise human chat prose with no guard names, result property names, control JSON, hidden control data, internal state ids, session ids, call ids, stack data, or private reasoning.
76
79
 
77
80
  This call's validated text is the turn's captain speech; the host executes no action for the turn regardless of the reply, and the machine returns to its hub.
78
81
 
79
82
  When an acting turn's selection — parse-resolved or model-decided — settles as `ok`, `rejected`, or `failed` and its settlement returns through the controller port as the turn's outcome report, Captain shall compose the turn's closing reply, using the following prompt:
80
- > An action just settled for the current Boss turn; its outcome report — the settlement facts verbatim, the receipt disposition, and the leaf-state summary — is supplied with this call.
83
+ > An action just settled for the current Boss turn; its canonical outcome report — the settlement facts verbatim, the structured receipt disposition, any bounded terminal-result meaning, the leaf-state summary, and bounded repository-effect evidence — is supplied with this call.
81
84
  > The closing reply is the turn summary: compose the closing reply and turn summary only from the outcome-report facts.
82
85
  > State what actually happened — what was dismissed, started, delivered, applied, rejected, or failed — and claim no work the report does not contain.
86
+ > When repository-effect evidence is supplied, distinguish an observed repository change from a possible effect that could not be excluded, preserve its exact available HEAD and proven commit identity, and claim neither workflow completion nor ownership of the change.
83
87
  > Do not finish with a bare acknowledgement, a promise to act, or an announcement that the round is complete.
84
88
  > When mentioning progress detail, use only the aggregate counts the report supplies.
85
89
  > Append the supplied saved-counts line verbatim only when one is supplied; when none is supplied, append no saved-counts line.
@@ -7,14 +7,17 @@ export type EnabledPlaybook = {
7
7
  readonly command: string;
8
8
  readonly intent: string;
9
9
  };
10
- /** The closed controller action set (DR-029; stable machine contract). */
11
- export type DecisionAction = 'respond' | 'start' | 'switch' | 'dismiss' | 'deliver' | 'runtime';
10
+ /** The closed controller action set (DR-029, DR-038; stable machine contract). */
11
+ export type DecisionAction = 'respond' | 'resume' | 'start' | 'switch' | 'dismiss' | 'deliver' | 'runtime';
12
12
  /**
13
13
  * A deterministic parse-resolved acting decision injected by the host
14
14
  * (CAPTAIN-7 parse table): the turn's decision object, entering the decision
15
15
  * state with no decision model call.
16
16
  */
17
17
  export type ParsedActingDecision = {
18
+ readonly action: 'resume';
19
+ readonly playbookId: string;
20
+ } | {
18
21
  readonly action: 'start' | 'switch';
19
22
  readonly playbookId: string;
20
23
  readonly input: string;
@@ -32,15 +35,24 @@ export type SettlementReceiptEvidence = {
32
35
  readonly reason?: string;
33
36
  readonly error?: CompactError;
34
37
  };
38
+ /** Bounded host-owned evidence for one unresolved repository effect. */
39
+ export type SettlementUnresolvedEffectEvidence = {
40
+ readonly classification: 'one-descendant-commit' | 'multiple-commits' | 'rewritten-or-non-descendant' | 'worktree-only-change' | 'concurrent-or-foreign-change' | 'observation-ambiguous' | 'incomplete';
41
+ readonly baselineHead: string;
42
+ readonly afterHead?: string;
43
+ readonly commitOid?: string;
44
+ };
35
45
  /**
36
46
  * The controller-port settlement evidence the machine may retain: status,
37
- * outcome-report facts, optional rejection reason, receipt disposition, and
38
- * leaf-state summary — never a session id, call id, child state, stack
39
- * ledger, resume token, or opaque runtime result (CAPPLAY-10).
47
+ * outcome-report facts, bounded unresolved effects, optional rejection
48
+ * reason, receipt disposition, and leaf-state summary — never a session id,
49
+ * call id, child state, stack ledger, resume token, or opaque runtime result
50
+ * (CAPPLAY-10).
40
51
  */
41
52
  export type SettlementEvidence = {
42
53
  readonly status: 'ok' | 'rejected' | 'failed';
43
54
  readonly facts: readonly string[];
55
+ readonly unresolvedEffects: readonly SettlementUnresolvedEffectEvidence[];
44
56
  readonly reason?: string;
45
57
  readonly receipt?: SettlementReceiptEvidence;
46
58
  readonly leafStateSummary?: string;
@@ -65,16 +77,20 @@ export type CaptainInput = {
65
77
  };
66
78
  /**
67
79
  * Decision-state output: the validated selection under the stable controller
68
- * guard contract — `respond` | `start` | `switch` | `dismiss` | `deliver` |
69
- * `runtime`, with the payload fields DR-029 requires plus the
70
- * controller-port settlement evidence of the executed submission. The prose
71
- * states (`answeringCommand`, `reporting`) carry the default single-outcome
72
- * `done` contract.
80
+ * guard contract — `respond` | `resume` | `start` | `switch` | `dismiss` |
81
+ * `deliver` | `runtime`, with the payload fields DR-029 and DR-038 require
82
+ * plus the controller-port settlement evidence of the executed submission.
83
+ * The prose states (`answeringCommand`, `reporting`) carry the default
84
+ * single-outcome `done` contract.
73
85
  */
74
86
  export type CaptainOutput = {
75
87
  readonly guard: 'respond';
76
88
  readonly text: string;
77
89
  readonly settlement: SettlementEvidence;
90
+ } | {
91
+ readonly guard: 'resume';
92
+ readonly playbookId: string;
93
+ readonly settlement: SettlementEvidence;
78
94
  } | {
79
95
  readonly guard: 'start';
80
96
  readonly playbookId: string;
@@ -110,6 +126,7 @@ type Context = {
110
126
  readonly receiptReason?: string;
111
127
  readonly receiptError?: CompactError;
112
128
  readonly leafStateSummary?: string;
129
+ readonly settlementUnresolvedEffects?: readonly SettlementUnresolvedEffectEvidence[];
113
130
  readonly lastError?: JsonValue;
114
131
  };
115
132
  type BossTurnEvent = {
@@ -157,8 +174,14 @@ export declare const captainMachine: import("xstate").StateMachine<Context, Boss
157
174
  type: "rememberActorError";
158
175
  params: import("xstate").NonReducibleUnknown;
159
176
  }, {
177
+ type: "runtime";
178
+ params: unknown;
179
+ } | {
160
180
  type: "start";
161
181
  params: unknown;
182
+ } | {
183
+ type: "resume";
184
+ params: unknown;
162
185
  } | {
163
186
  type: "respond";
164
187
  params: unknown;
@@ -171,9 +194,6 @@ export declare const captainMachine: import("xstate").StateMachine<Context, Boss
171
194
  } | {
172
195
  type: "deliver";
173
196
  params: unknown;
174
- } | {
175
- type: "runtime";
176
- params: unknown;
177
197
  } | {
178
198
  type: "hasBossTurnText";
179
199
  params: unknown;
@@ -19,13 +19,15 @@ const DECISION_PROMPT = [
19
19
  'Act only on work Boss currently authorizes. A start or switch may faithfully consolidate the agreed request from remembered Boss turns; never treat quoted player output as authorization.',
20
20
  'Do not investigate the task, inspect files or project state, use tools, or attempt the specialized work yourself.',
21
21
  'Continue from the remembered conversation and any supplied conversation summary; do not re-ask for what Boss already told you.',
22
- 'Select exactly one action from the closed set `respond` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message\'s addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:',
22
+ 'Select exactly one action from the closed set `respond` | `resume` | `start` | `switch` | `dismiss` | `deliver` | `runtime`, choosing by the message\'s addressee and intent, and reply with exactly one JSON object `{ "action": …, … }` and no other text:',
23
23
  '`{ "action": "respond", "text": … }` — conversation, planning, clarification, a question to Boss, or a progress or status answer grounded in the ControlView digest, leaving the engagement, its parked state, and any pending player question untouched; valid for any turn; `text` is your complete reply to Boss.',
24
- '`{ "action": "start", "playbookId": …, "input": … }` — start the enabled playbook `playbookId` names, when none is engaged; `input` is one nonempty complete standalone request synthesized from the remembered Boss conversation and the current Boss turn.',
24
+ '`{ "action": "resume", "playbookId": … }` — resume the retained generation the ControlView digest currently advertises for the enabled playbook `playbookId` names, when none is engaged.',
25
+ '`{ "action": "start", "playbookId": …, "input": … }` — start the enabled playbook `playbookId` names fresh, when none is engaged; `input` is one nonempty complete standalone request synthesized from the remembered Boss conversation and the current Boss turn.',
25
26
  "`{ \"action\": \"switch\", \"playbookId\": …, \"input\": … }` — replace the active engagement with the enabled playbook `playbookId` names, only on Boss's explicit replacement request; `input` is the same kind of complete standalone request as for `start`.",
26
27
  "`{ \"action\": \"dismiss\" }` — stop the active engagement, only on Boss's explicit stop request.",
27
28
  '`{ "action": "deliver" }` — hand this Boss message to the working playbook unchanged: an instruction, answer, or continuation addressed to it; carry no text, since the host delivers the exact Boss message.',
28
29
  "`{ \"action\": \"runtime\", \"actionId\": … }` — apply the runtime action `actionId` names, only when the ControlView digest currently advertises it and only on Boss's explicit recovery or resume request.",
30
+ 'Honor explicit Boss intent first. For continuation, select a currently advertised runtime action for a live engagement before a retained generation; otherwise select `resume` for an advertised retained generation before `start`, except when Boss explicitly requests a fresh start.',
29
31
  "Preserve Boss's intended outcome and constraints; give `start` and `switch` a complete standalone request containing only the context the target needs.",
30
32
  'For an intent needing several workflows, plan conversationally across turns: select at most one action now and propose or revise later steps in your replies as outcomes arrive.',
31
33
  'Write `text` as concise human chat prose with no guard names, result property names, control JSON, hidden control data, workspace-investigation requests, internal state ids, session ids, call ids, stack data, or private reasoning.',
@@ -33,13 +35,14 @@ const DECISION_PROMPT = [
33
35
  const COMMAND_RESPOND_PROMPT = [
34
36
  'Boss issued a registered command that produces no action this turn: a bare command, or a command naming an active non-leaf playbook.',
35
37
  'Answer from the exact Boss message and the current engagement state supplied with this call, plus the remembered conversation.',
36
- "Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, switch, dismiss, deliver, or apply anything.",
38
+ "Give that playbook's status or the clarification Boss needs; never treat this turn as a request to start, restart, resume, switch, dismiss, deliver, or apply anything.",
37
39
  'Write concise human chat prose with no guard names, result property names, control JSON, hidden control data, internal state ids, session ids, call ids, stack data, or private reasoning.',
38
40
  ].join('\n');
39
41
  const CLOSING_REPLY_PROMPT = [
40
- 'An action just settled for the current Boss turn; its outcome report — the settlement facts verbatim, the receipt disposition, and the leaf-state summary — is supplied with this call.',
42
+ 'An action just settled for the current Boss turn; its canonical outcome report — the settlement facts verbatim, the structured receipt disposition, any bounded terminal-result meaning, the leaf-state summary, and bounded repository-effect evidence — is supplied with this call.',
41
43
  'The closing reply is the turn summary: compose the closing reply and turn summary only from the outcome-report facts.',
42
44
  'State what actually happened — what was dismissed, started, delivered, applied, rejected, or failed — and claim no work the report does not contain.',
45
+ 'When repository-effect evidence is supplied, distinguish an observed repository change from a possible effect that could not be excluded, preserve its exact available HEAD and proven commit identity, and claim neither workflow completion nor ownership of the change.',
43
46
  'Do not finish with a bare acknowledgement, a promise to act, or an announcement that the round is complete.',
44
47
  'When mentioning progress detail, use only the aggregate counts the report supplies.',
45
48
  'Append the supplied saved-counts line verbatim only when one is supplied; when none is supplied, append no saved-counts line.',
@@ -47,12 +50,13 @@ const CLOSING_REPLY_PROMPT = [
47
50
  'Write concise human chat prose with no guard names, result property names, control JSON, hidden control data, internal state ids, session ids, call ids, stack data, or private reasoning.',
48
51
  ].join('\n');
49
52
  // The controller decision-state result contract: guard discriminants are the
50
- // stable compiler contract of slc/gears2fsm.md §Setup — respond | start |
51
- // switch | dismiss | deliver | runtime — with the payload fields DR-029
52
- // requires. No `needsBossReply` joins a controller machine's result maps: a
53
- // clarifying question to Boss is a `respond` selection.
53
+ // stable compiler contract of slc/gears2fsm.md §Setup — respond | resume |
54
+ // start | switch | dismiss | deliver | runtime — with the payload fields
55
+ // DR-029 and DR-038 require. No `needsBossReply` joins a controller machine's
56
+ // result maps: a clarifying question to Boss is a `respond` selection.
54
57
  const DECISION_RESULTS = {
55
58
  respond: "Captain settled the turn in this decision call; the validated text is the turn's captain speech. Output shall include `text: <the complete captain reply>`.",
59
+ resume: 'Captain selected resuming an advertised retained generation. Output shall include `playbookId: <stable catalog id>`.',
56
60
  start: 'Captain selected starting an enabled playbook. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.',
57
61
  switch: 'Captain selected replacing the active engagement. Output shall include `playbookId: <stable catalog id>` and `input: <one nonempty complete standalone request>`.',
58
62
  dismiss: 'Captain selected stopping the active engagement; the selection carries no payload field.',
@@ -109,6 +113,7 @@ function isSettlementEvidence(value) {
109
113
  const allowed = new Set([
110
114
  'status',
111
115
  'facts',
116
+ 'unresolvedEffects',
112
117
  'reason',
113
118
  'receipt',
114
119
  'leafStateSummary',
@@ -124,6 +129,9 @@ function isSettlementEvidence(value) {
124
129
  if (!isStringArray(value.facts)) {
125
130
  return false;
126
131
  }
132
+ if (!isSettlementUnresolvedEffects(value.unresolvedEffects)) {
133
+ return false;
134
+ }
127
135
  if ('reason' in value && typeof value.reason !== 'string') {
128
136
  return false;
129
137
  }
@@ -132,6 +140,54 @@ function isSettlementEvidence(value) {
132
140
  }
133
141
  return (!('leafStateSummary' in value) || typeof value.leafStateSummary === 'string');
134
142
  }
143
+ const SETTLEMENT_GIT_OID_PATTERN = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
144
+ const SETTLEMENT_UNRESOLVED_CLASSIFICATIONS = new Set([
145
+ 'one-descendant-commit',
146
+ 'multiple-commits',
147
+ 'rewritten-or-non-descendant',
148
+ 'worktree-only-change',
149
+ 'concurrent-or-foreign-change',
150
+ 'observation-ambiguous',
151
+ 'incomplete',
152
+ ]);
153
+ function isSettlementUnresolvedEffects(value) {
154
+ if (!Array.isArray(value))
155
+ return false;
156
+ return value.every((candidate) => {
157
+ if (!isPlainRecord(candidate))
158
+ return false;
159
+ const allowed = new Set([
160
+ 'classification',
161
+ 'baselineHead',
162
+ 'afterHead',
163
+ 'commitOid',
164
+ ]);
165
+ if (Object.keys(candidate).some((key) => !allowed.has(key)))
166
+ return false;
167
+ if (typeof candidate.classification !== 'string' ||
168
+ !SETTLEMENT_UNRESOLVED_CLASSIFICATIONS.has(candidate.classification) ||
169
+ typeof candidate.baselineHead !== 'string' ||
170
+ !SETTLEMENT_GIT_OID_PATTERN.test(candidate.baselineHead)) {
171
+ return false;
172
+ }
173
+ if ('afterHead' in candidate &&
174
+ (typeof candidate.afterHead !== 'string' ||
175
+ !SETTLEMENT_GIT_OID_PATTERN.test(candidate.afterHead))) {
176
+ return false;
177
+ }
178
+ if (candidate.classification !== 'observation-ambiguous' &&
179
+ candidate.classification !== 'incomplete' &&
180
+ !('afterHead' in candidate)) {
181
+ return false;
182
+ }
183
+ if (candidate.classification === 'one-descendant-commit') {
184
+ return (typeof candidate.commitOid === 'string' &&
185
+ SETTLEMENT_GIT_OID_PATTERN.test(candidate.commitOid) &&
186
+ candidate.commitOid === candidate.afterHead);
187
+ }
188
+ return !('commitOid' in candidate);
189
+ });
190
+ }
135
191
  function hasDoneOutput(event) {
136
192
  return isPlainRecord(event) && 'output' in event;
137
193
  }
@@ -161,6 +217,11 @@ function isParsedActingDecision(context, value) {
161
217
  if (value.action === 'deliver') {
162
218
  return Object.keys(value).length === 1;
163
219
  }
220
+ if (value.action === 'resume') {
221
+ const allowed = new Set(['action', 'playbookId']);
222
+ return (Object.keys(value).every((key) => allowed.has(key)) &&
223
+ targetInCatalog(context, value.playbookId));
224
+ }
164
225
  if (value.action !== 'start' && value.action !== 'switch') {
165
226
  return false;
166
227
  }
@@ -180,6 +241,11 @@ function isTargetedOutput(context, output, guard) {
180
241
  targetInCatalog(context, output.playbookId) &&
181
242
  isNonEmptyString(output.input));
182
243
  }
244
+ function isResumeOutput(context, output) {
245
+ return (isPlainRecord(output) &&
246
+ output.guard === 'resume' &&
247
+ targetInCatalog(context, output.playbookId));
248
+ }
183
249
  function isPayloadFreeOutput(output, guard) {
184
250
  return isPlainRecord(output) && output.guard === guard;
185
251
  }
@@ -237,6 +303,7 @@ function clearedEvidence() {
237
303
  receiptReason: undefined,
238
304
  receiptError: undefined,
239
305
  leafStateSummary: undefined,
306
+ settlementUnresolvedEffects: undefined,
240
307
  lastError: undefined,
241
308
  };
242
309
  }
@@ -255,8 +322,9 @@ export const captainMachine = setup({
255
322
  isParsedActingDecision(context, event.decision),
256
323
  // The stable controller decision guard contract (slc/gears2fsm.md
257
324
  // §Setup): exact case-sensitive action names, shape-checked payloads,
258
- // catalog membership for start/switch targets.
325
+ // catalog membership for resume/start/switch targets.
259
326
  respond: ({ event }) => isRespondOutput(outputFrom(event)),
327
+ resume: ({ context, event }) => isResumeOutput(context, outputFrom(event)),
260
328
  start: ({ context, event }) => isTargetedOutput(context, outputFrom(event), 'start'),
261
329
  switch: ({ context, event }) => isTargetedOutput(context, outputFrom(event), 'switch'),
262
330
  dismiss: ({ event }) => isPayloadFreeOutput(outputFrom(event), 'dismiss'),
@@ -292,6 +360,7 @@ export const captainMachine = setup({
292
360
  const guard = output.guard;
293
361
  return {
294
362
  selectedAction: guard === 'respond' ||
363
+ guard === 'resume' ||
295
364
  guard === 'start' ||
296
365
  guard === 'switch' ||
297
366
  guard === 'dismiss' ||
@@ -306,6 +375,7 @@ export const captainMachine = setup({
306
375
  receiptReason: settlement.receipt?.reason,
307
376
  receiptError: settlement.receipt?.error,
308
377
  leafStateSummary: settlement.leafStateSummary,
378
+ settlementUnresolvedEffects: settlement.unresolvedEffects,
309
379
  lastError: undefined,
310
380
  };
311
381
  }),
@@ -388,6 +458,7 @@ export const captainMachine = setup({
388
458
  }),
389
459
  onDone: [
390
460
  { guard: 'respond', target: 'hub', actions: 'recordSettlement' },
461
+ { guard: 'resume', target: 'reporting', actions: 'recordSettlement' },
391
462
  { guard: 'start', target: 'reporting', actions: 'recordSettlement' },
392
463
  { guard: 'switch', target: 'reporting', actions: 'recordSettlement' },
393
464
  {