@sublang/playbook 11.0.0 → 12.1.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 (49) hide show
  1. package/docs/cli.md +9 -6
  2. package/docs/configuration.md +15 -1
  3. package/docs/embedding.md +83 -1
  4. package/package.json +28 -3
  5. package/reference/sdlc/code.playbook/bin/repository-effects.js +501 -170
  6. package/reference/sdlc/code.playbook/bin/session-store.js +4 -1
  7. package/reference/sdlc/code.playbook/code.fsm.d.ts +22 -19
  8. package/reference/sdlc/code.playbook/code.fsm.js +116 -52
  9. package/reference/sdlc/code.playbook/code.fsm.ts +149 -64
  10. package/reference/sdlc/code.playbook/code.gears.md +40 -20
  11. package/reference/sdlc/code.playbook/code.playbook.js +23 -2
  12. package/reference/sdlc/code.playbook/code.playbook.ts +23 -2
  13. package/reference/sdlc/code.playbook/host-capabilities.d.ts +291 -0
  14. package/reference/sdlc/code.playbook/host-capabilities.js +40 -0
  15. package/reference/sdlc/code.playbook/playbook.config.template.yaml +18 -2
  16. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +13 -6
  17. package/reference/sdlc/decide.playbook/decide.fsm.js +54 -27
  18. package/reference/sdlc/decide.playbook/decide.fsm.ts +68 -29
  19. package/reference/sdlc/decide.playbook/decide.gears.md +25 -19
  20. package/reference/sdlc/decide.playbook/decide.playbook.js +11 -3
  21. package/reference/sdlc/decide.playbook/decide.playbook.ts +11 -3
  22. package/reference/sdlc/decide.playbook/decide.registry.js +1 -1
  23. package/reference/sdlc/decide.playbook/decide.registry.ts +1 -1
  24. package/reference/sdlc/dev.md +52 -0
  25. package/reference/sdlc/dev.playbook/dev.fsm.d.ts +261 -0
  26. package/reference/sdlc/dev.playbook/dev.fsm.js +723 -0
  27. package/reference/sdlc/dev.playbook/dev.fsm.ts +988 -0
  28. package/reference/sdlc/dev.playbook/dev.gears.md +91 -0
  29. package/reference/sdlc/dev.playbook/dev.playbook.d.ts +21 -0
  30. package/reference/sdlc/dev.playbook/dev.playbook.js +143 -0
  31. package/reference/sdlc/dev.playbook/dev.playbook.ts +246 -0
  32. package/reference/sdlc/dev.playbook/dev.registry.d.ts +40 -0
  33. package/reference/sdlc/dev.playbook/dev.registry.js +64 -0
  34. package/reference/sdlc/dev.playbook/dev.registry.ts +120 -0
  35. package/reference/sdlc/review.playbook/review.fsm.d.ts +15 -2
  36. package/reference/sdlc/review.playbook/review.fsm.js +77 -27
  37. package/reference/sdlc/review.playbook/review.fsm.ts +96 -30
  38. package/reference/sdlc/review.playbook/review.gears.md +52 -26
  39. package/reference/sdlc/review.playbook/review.playbook.js +17 -7
  40. package/reference/sdlc/review.playbook/review.playbook.ts +17 -7
  41. package/reference/sdlc/review.playbook/review.registry.js +1 -1
  42. package/reference/sdlc/review.playbook/review.registry.ts +1 -1
  43. package/slc/gears2fsm.md +20 -0
  44. package/slc/link.md +28 -5
  45. package/slc/text2gears.md +21 -1
  46. package/src/xstate-playbook-runtime.js +5 -2
  47. package/src/xstate-playbook-runtime.ts +5 -2
  48. package/src/xstate-runtime.js +13 -1
  49. package/src/xstate-runtime.ts +13 -1
@@ -43,7 +43,13 @@ type BossReplies = Partial<Record<ResumableStateId, string>>;
43
43
  type PendingBossQuestionParams = Omit<PendingBossQuestion, 'questionId'>;
44
44
 
45
45
  export interface ReviewSuccessOutput {
46
- approvedCommit: 'latest';
46
+ evaluatedRevision: string;
47
+ noUnsettledFindings: true;
48
+ }
49
+
50
+ export interface DecideSuccessOutput {
51
+ decideCommit: string;
52
+ evaluatedRevision: string;
47
53
  noUnsettledFindings: true;
48
54
  }
49
55
 
@@ -57,7 +63,7 @@ export interface DecideFailureOutput {
57
63
  };
58
64
  }
59
65
 
60
- export type DecideOutput = ReviewSuccessOutput | DecideFailureOutput;
66
+ export type DecideOutput = DecideSuccessOutput | DecideFailureOutput;
61
67
 
62
68
  interface ChildFailure {
63
69
  status: 'aborted' | 'error';
@@ -73,6 +79,7 @@ export interface DecideContext {
73
79
  coderProposal?: string;
74
80
  reviewerProposal?: string;
75
81
  latestCommit?: string;
82
+ coderOutput?: string;
76
83
  reviewResult?: ReviewSuccessOutput;
77
84
  reviewFailure?: ChildFailure;
78
85
  lastResult?: PlayerOutput;
@@ -105,6 +112,7 @@ export interface PlayerInput {
105
112
  result: Record<string, string>;
106
113
  role: Role;
107
114
  callerTopic?: string;
115
+ reviewerProposal?: string;
108
116
  pendingBossQuestion?: PendingBossQuestion;
109
117
  bossReply?: string;
110
118
  }
@@ -149,37 +157,48 @@ const INDEPENDENT_PROPOSAL_PROMPT = [
149
157
  'Do not change any files.',
150
158
  ].join('\n');
151
159
 
152
- const COMMIT_CODER_PROMPT = [
153
- 'Turn your proposal into the necessary spec items or DRs.',
160
+ const SYNTHESIZE_COMMIT_PROMPT = [
161
+ "Synthesize your independent proposal with Reviewer's proposal below.",
162
+ 'Keep to the original topic below and follow what it asks.',
163
+ 'Keep the best, essential parts of either proposal and reject any point that is unsound, unnecessary, or outside the topic.',
164
+ 'Turn the resulting design into the necessary DRs and/or spec items.',
154
165
  'Follow @specs/meta.md and update @specs/map.md when needed.',
155
- "Do not inspect or incorporate Reviewer's proposal before this commit.",
156
- 'Do not change code or implement the proposal.',
166
+ 'Do not change code or implement the design.',
157
167
  '',
158
168
  'Commit the result as one new commit, following @specs/packages/git.md.',
159
169
  'Make the commit message explain concisely what changed and why.',
160
- 'Coder is <coder-llm>; format the model token in conventional human form.',
170
+ 'Identify every new commit you make.',
171
+ 'Coder is <coder-llm> and Reviewer is <reviewer-llm>.',
172
+ '',
173
+ '> Original topic: <caller-topic>',
174
+ "> Reviewer's independent proposal: <reviewer-proposal>",
161
175
  ].join('\n');
162
176
 
163
177
  const REVIEW_INPUT_TEMPLATE = [
164
- 'Review the latest commit as a spec-design change against the initial intent.',
165
- 'Compare it with your independent proposal and take the best of both.',
166
- 'Make your suggestions.',
167
- '',
168
- 'Initial intent: <caller-topic>.',
169
- "Coder's independent proposal: <coder-proposal>.",
178
+ '> Original intent: <caller-topic>',
179
+ '> Review scope: the `decide`-owned commit <decide-commit> and its resulting repository state.',
180
+ '> Coder output: <coder-output>',
170
181
  ].join('\n');
171
182
 
172
183
  function composeReviewInput(
173
184
  callerTopic: string | undefined,
174
- coderProposal: string | undefined,
185
+ decideCommit: string | undefined,
186
+ coderOutput: string | undefined,
175
187
  ): string {
176
188
  const fields: Readonly<Record<string, string>> = {
177
189
  '<caller-topic>': callerTopic ?? '',
178
- '<coder-proposal>': coderProposal ?? '',
190
+ '<decide-commit>': decideCommit ?? '',
191
+ '<coder-output>': coderOutput ?? '',
179
192
  };
180
193
  return REVIEW_INPUT_TEMPLATE.replace(
181
- /<caller-topic>|<coder-proposal>/g,
182
- (placeholder) => fields[placeholder],
194
+ /<caller-topic>|<decide-commit>|<coder-output>/g,
195
+ (placeholder, offset: number, source: string) => {
196
+ const value = fields[placeholder];
197
+ const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
198
+ return source.slice(lineStart, offset).startsWith('> ')
199
+ ? value.replaceAll('\n', '\n> ')
200
+ : value;
201
+ },
183
202
  );
184
203
  }
185
204
 
@@ -370,20 +389,24 @@ function isPlaybookState(value: unknown): boolean {
370
389
  );
371
390
  }
372
391
 
392
+ // DECIDE supplied `review` a scope rooted at the `decide`-owned commit, so a
393
+ // result applies to that scope only when it is the exact declared terminal
394
+ // contract: the evaluated repository revision plus the affirmative
395
+ // no-unsettled-findings fact. Any other shape does not establish those facts.
373
396
  function reviewSuccessFrom(event: unknown): ReviewSuccessOutput | undefined {
374
397
  const output = playbookOutputOf(event);
375
398
  if (!isPlainRecord(output)) return undefined;
376
- if (!isExactKeys(output, ['approvedCommit', 'noUnsettledFindings'])) {
399
+ if (!isExactKeys(output, ['evaluatedRevision', 'noUnsettledFindings'])) {
377
400
  return undefined;
378
401
  }
379
402
  if (
380
- output.approvedCommit !== 'latest' ||
403
+ !isNonEmptyString(output.evaluatedRevision) ||
381
404
  output.noUnsettledFindings !== true
382
405
  ) {
383
406
  return undefined;
384
407
  }
385
408
  return {
386
- approvedCommit: 'latest',
409
+ evaluatedRevision: output.evaluatedRevision,
387
410
  noUnsettledFindings: true,
388
411
  };
389
412
  }
@@ -537,6 +560,7 @@ export const decideMachine = setup({
537
560
  coderProposal: undefined,
538
561
  reviewerProposal: undefined,
539
562
  latestCommit: undefined,
563
+ coderOutput: undefined,
540
564
  reviewResult: undefined,
541
565
  reviewFailure: undefined,
542
566
  lastResult: undefined,
@@ -552,6 +576,7 @@ export const decideMachine = setup({
552
576
  coderProposal: undefined,
553
577
  reviewerProposal: undefined,
554
578
  latestCommit: undefined,
579
+ coderOutput: undefined,
555
580
  reviewResult: undefined,
556
581
  reviewFailure: undefined,
557
582
  lastResult: undefined,
@@ -583,6 +608,7 @@ export const decideMachine = setup({
583
608
  }),
584
609
  rememberCommit: assign({
585
610
  latestCommit: ({ event }) => outputOf(event).latestCommit,
611
+ coderOutput: ({ event }) => outputOf(event).coderOutput,
586
612
  lastResult: ({ event }) => outputOf(event),
587
613
  lastError: undefined,
588
614
  }),
@@ -603,7 +629,7 @@ export const decideMachine = setup({
603
629
  error: {
604
630
  name: 'ReviewProtocolError',
605
631
  message:
606
- 'REVIEW returned without an approved latest commit and a no-findings result.',
632
+ 'REVIEW returned without the evaluated repository revision and a no-unsettled-findings result for the supplied scope.',
607
633
  },
608
634
  },
609
635
  lastError: undefined,
@@ -678,7 +704,16 @@ export const decideMachine = setup({
678
704
  initial: 'ready',
679
705
  context: (): DecideContext => ({}),
680
706
  output: ({ context }): DecideOutput => {
681
- if (context.reviewResult) return context.reviewResult;
707
+ if (context.reviewResult) {
708
+ if (!context.latestCommit) {
709
+ throw new Error('DECIDE completed without its decide-owned commit');
710
+ }
711
+ return {
712
+ decideCommit: context.latestCommit,
713
+ evaluatedRevision: context.reviewResult.evaluatedRevision,
714
+ noUnsettledFindings: true,
715
+ };
716
+ }
682
717
  if (!context.latestCommit || !context.reviewFailure) {
683
718
  throw new Error('DECIDE reached a final state without a result');
684
719
  }
@@ -747,7 +782,7 @@ export const decideMachine = setup({
747
782
  prompt: INDEPENDENT_PROPOSAL_PROMPT,
748
783
  result: withNeedsBossReply({
749
784
  proposed:
750
- 'Coder completed an independent proposal. Output shall include `coderProposal: <verbatim final text>`.',
785
+ 'Coder affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `coderProposal: <verbatim final text>`.',
751
786
  }),
752
787
  callerTopic: context.callerTopic,
753
788
  ...bossReplyFields(context, 'askCoderProposal'),
@@ -887,7 +922,7 @@ export const decideMachine = setup({
887
922
  prompt: INDEPENDENT_PROPOSAL_PROMPT,
888
923
  result: withNeedsBossReply({
889
924
  proposed:
890
- 'Reviewer completed an independent proposal. Output shall include `reviewerProposal: <verbatim final text>`.',
925
+ 'Reviewer affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `reviewerProposal: <verbatim final text>`.',
891
926
  }),
892
927
  callerTopic: context.callerTopic,
893
928
  ...bossReplyFields(context, 'askReviewerProposal'),
@@ -1014,11 +1049,12 @@ export const decideMachine = setup({
1014
1049
  commitCoderProposal: {
1015
1050
  id: 'commitCoderProposal',
1016
1051
  tags: 'playbook.busy',
1017
- description: 'Coder writes and commits Coder’s independent proposal.',
1052
+ description: 'Coder synthesizes both proposals and commits the design.',
1018
1053
  meta: {
1019
1054
  playbook: {
1020
1055
  stateId: 'commitCoderProposal',
1021
- description: 'Coder writes and commits Coder’s independent proposal.',
1056
+ description:
1057
+ 'Coder synthesizes both proposals and commits the design.',
1022
1058
  role: 'coder',
1023
1059
  },
1024
1060
  },
@@ -1028,11 +1064,13 @@ export const decideMachine = setup({
1028
1064
  stateId: 'commitCoderProposal',
1029
1065
  sourceItem: 'DECIDE-3',
1030
1066
  role: 'coder',
1031
- prompt: COMMIT_CODER_PROMPT,
1067
+ prompt: SYNTHESIZE_COMMIT_PROMPT,
1032
1068
  result: withNeedsBossReply({
1033
1069
  committed:
1034
- "Coder committed Coder's proposal. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.",
1070
+ 'Coder synthesized both proposals and committed the resulting design as one new commit. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.',
1035
1071
  }),
1072
+ callerTopic: context.callerTopic,
1073
+ reviewerProposal: context.reviewerProposal,
1036
1074
  ...bossReplyFields(context, 'commitCoderProposal'),
1037
1075
  }),
1038
1076
  onDone: [
@@ -1141,7 +1179,8 @@ export const decideMachine = setup({
1141
1179
  playbookId: 'review',
1142
1180
  text: composeReviewInput(
1143
1181
  context.callerTopic,
1144
- context.coderProposal,
1182
+ context.latestCommit,
1183
+ context.coderOutput,
1145
1184
  ),
1146
1185
  }),
1147
1186
  onDone: [
@@ -12,7 +12,8 @@
12
12
 
13
13
  The caller supplies a topic as `callerTopic`.
14
14
  Coder and Reviewer receive the complete topic concurrently and independently.
15
- Neither role's player receives the other role's proposal before both proposals finish and Coder commits Coder's own proposal.
15
+ Neither role's player receives the other role's proposal before both proposals are complete.
16
+ A proposal is complete only when its player affirmatively provides a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome.
16
17
  A Boss interrupt during the parallel proposal pair restarts the complete pair with the new `callerTopic`.
17
18
 
18
19
  ### DECIDE-1
@@ -30,7 +31,7 @@ When the caller gives a topic, Captain shall relay the complete topic to Coder i
30
31
  > Do not change any files.
31
32
 
32
33
  Results:
33
- - `proposed`: Coder completed an independent proposal. Output shall include `coderProposal: <verbatim final text>`.
34
+ - `proposed`: Coder affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `coderProposal: <verbatim final text>`.
34
35
 
35
36
  ### DECIDE-2
36
37
 
@@ -47,39 +48,44 @@ When the caller gives a topic, Captain shall relay the complete topic to Reviewe
47
48
  > Do not change any files.
48
49
 
49
50
  Results:
50
- - `proposed`: Reviewer completed an independent proposal. Output shall include `reviewerProposal: <verbatim final text>`.
51
+ - `proposed`: Reviewer affirmatively provided a complete design proposal; a progress report, status update, or promise of a later proposal supports no proposal outcome. Output shall include `reviewerProposal: <verbatim final text>`.
51
52
 
52
- ## Commit Coder's proposal
53
+ ## Synthesize and commit
53
54
 
54
55
  ### DECIDE-3
55
56
 
56
- When both independent proposals are complete, Captain shall prompt Coder:
57
+ When both independent proposals are complete, Captain shall relay the complete topic and Reviewer's complete proposal to Coder under their own labels in quotes and prompt Coder:
57
58
 
58
- > Turn your proposal into the necessary spec items or DRs.
59
+ > Synthesize your independent proposal with Reviewer's proposal below.
60
+ > Keep to the original topic below and follow what it asks.
61
+ > Keep the best, essential parts of either proposal and reject any point that is unsound, unnecessary, or outside the topic.
62
+ > Turn the resulting design into the necessary DRs and/or spec items.
59
63
  > Follow @specs/meta.md and update @specs/map.md when needed.
60
- > Do not inspect or incorporate Reviewer's proposal before this commit.
61
- > Do not change code or implement the proposal.
64
+ > Do not change code or implement the design.
62
65
  >
63
66
  > Commit the result as one new commit, following @specs/packages/git.md.
64
67
  > Make the commit message explain concisely what changed and why.
65
- > Coder is <coder-llm>; format the model token in conventional human form.
68
+ > Identify every new commit you make.
69
+ > Coder is <coder-llm> and Reviewer is <reviewer-llm>.
70
+ >
71
+ > > Original topic: <caller-topic>
72
+ > > Reviewer's independent proposal: <reviewer-proposal>
66
73
 
67
74
  Results:
68
- - `committed`: Coder committed Coder's proposal. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.
75
+ - `committed`: Coder synthesized both proposals and committed the resulting design as one new commit. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.
76
+
77
+ No transition depends on a fixed presentation format of either player's reply; the repository-effect receipt is the authoritative identity of Coder's new `decide`-owned commit.
69
78
 
70
79
  ## Review
71
80
 
72
81
  ### DECIDE-4
73
82
 
74
- When Coder commits, Captain shall call playbook `review`:
83
+ When Coder commits, Captain shall call playbook `review` with the following input in quotes:
75
84
 
76
- > Review the latest commit as a spec-design change against the initial intent.
77
- > Compare it with your independent proposal and take the best of both.
78
- > Make your suggestions.
79
- >
80
- > Initial intent: <caller-topic>.
81
- > Coder's independent proposal: <coder-proposal>.
85
+ > > Original intent: <caller-topic>
86
+ > > Review scope: the `decide`-owned commit <decide-commit> and its resulting repository state.
87
+ > > Coder output: <coder-output>
82
88
 
83
- The successful child output is DECIDE's terminal output.
84
- An authored child abort, failure, or invalid approval terminates with the failure and `latestCommit` reported to the caller.
89
+ `decide` is complete only when `review` returns a result that applies to the supplied review scope, gives the exact evaluated repository revision, and affirmatively establishes that no unsettled findings remain; `decide` then returns the `decide`-owned commit and that evaluated revision to the caller.
90
+ An authored `review` abort or failure, or a terminal result that does not establish those facts, terminates with the failure and the last `decide`-owned commit reported to the caller.
85
91
  Any other nested-call error parks `decide` as failed and retains the control-plane error.
@@ -123,7 +123,10 @@ const TRACE_TOPIC = 'playbook.trace';
123
123
  const UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID = 'reconcile:unresolved-effect';
124
124
  const UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID = 'abandon:unresolved-effect';
125
125
  const CONTINUATION_PREAMBLE = 'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
126
- const PLACEHOLDER_FIELDS = [['<caller-topic>', 'callerTopic']];
126
+ const PLACEHOLDER_FIELDS = [
127
+ ['<caller-topic>', 'callerTopic'],
128
+ ['<reviewer-proposal>', 'reviewerProposal'],
129
+ ];
127
130
  const VERBATIM_PAYLOAD_FIELDS = new Set([
128
131
  'coderProposal',
129
132
  'reviewerProposal',
@@ -151,12 +154,17 @@ function composePlayerPrompt(input, promptIdentity) {
151
154
  if (input.prompt.includes('<coder-llm>')) {
152
155
  replacements.set('<coder-llm>', promptIdentity('coder'));
153
156
  }
154
- const body = input.prompt.replace(/<caller-topic>|<coder-llm>/g, (placeholder, offset, source) => {
157
+ if (input.prompt.includes('<reviewer-llm>')) {
158
+ replacements.set('<reviewer-llm>', promptIdentity('reviewer'));
159
+ }
160
+ const body = input.prompt.replace(/<caller-topic>|<reviewer-proposal>|<coder-llm>|<reviewer-llm>/g, (placeholder, offset, source) => {
155
161
  const value = replacements.get(placeholder);
156
162
  if (value === undefined)
157
163
  return placeholder;
164
+ // A labelled quoted relay keeps a multiline value inside its authored
165
+ // blockquote, so continuation lines re-enter the quote marker.
158
166
  const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
159
- return source.slice(lineStart, offset) === '> '
167
+ return source.slice(lineStart, offset).startsWith('> ')
160
168
  ? value.replaceAll('\n', '\n> ')
161
169
  : value;
162
170
  });
@@ -284,7 +284,10 @@ const CONTINUATION_PREAMBLE =
284
284
  'You previously paused this task to ask Boss a question; Boss has now replied. Continue the same task using the reply below.';
285
285
 
286
286
  const PLACEHOLDER_FIELDS: ReadonlyArray<readonly [string, keyof PlayerInput]> =
287
- [['<caller-topic>', 'callerTopic']];
287
+ [
288
+ ['<caller-topic>', 'callerTopic'],
289
+ ['<reviewer-proposal>', 'reviewerProposal'],
290
+ ];
288
291
 
289
292
  const VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string> = new Set([
290
293
  'coderProposal',
@@ -322,13 +325,18 @@ function composePlayerPrompt(
322
325
  if (input.prompt.includes('<coder-llm>')) {
323
326
  replacements.set('<coder-llm>', promptIdentity('coder'));
324
327
  }
328
+ if (input.prompt.includes('<reviewer-llm>')) {
329
+ replacements.set('<reviewer-llm>', promptIdentity('reviewer'));
330
+ }
325
331
  const body = input.prompt.replace(
326
- /<caller-topic>|<coder-llm>/g,
332
+ /<caller-topic>|<reviewer-proposal>|<coder-llm>|<reviewer-llm>/g,
327
333
  (placeholder, offset: number, source: string) => {
328
334
  const value = replacements.get(placeholder);
329
335
  if (value === undefined) return placeholder;
336
+ // A labelled quoted relay keeps a multiline value inside its authored
337
+ // blockquote, so continuation lines re-enter the quote marker.
330
338
  const lineStart = source.lastIndexOf('\n', offset - 1) + 1;
331
- return source.slice(lineStart, offset) === '> '
339
+ return source.slice(lineStart, offset).startsWith('> ')
332
340
  ? value.replaceAll('\n', '\n> ')
333
341
  : value;
334
342
  },
@@ -41,7 +41,7 @@ export function validateDecideOptions(optionSlice) {
41
41
  export const decidePlaybookRegistryEntry = {
42
42
  id: 'decide',
43
43
  command: 'decide',
44
- intent: 'turn independent Coder and Reviewer proposals into an approved spec-design commit',
44
+ intent: 'synthesize independent Coder and Reviewer proposals into an approved spec-design commit',
45
45
  artifactSchema: 3,
46
46
  runtimeProfile: Object.freeze({
47
47
  kind: 'bespoke',
@@ -95,7 +95,7 @@ export const decidePlaybookRegistryEntry: DecidePlaybookRegistryEntry = {
95
95
  id: 'decide',
96
96
  command: 'decide',
97
97
  intent:
98
- 'turn independent Coder and Reviewer proposals into an approved spec-design commit',
98
+ 'synthesize independent Coder and Reviewer proposals into an approved spec-design commit',
99
99
  artifactSchema: 3,
100
100
  runtimeProfile: Object.freeze({
101
101
  kind: 'bespoke',
@@ -0,0 +1,52 @@
1
+ <!-- SPDX-License-Identifier: Apache-2.0 -->
2
+ <!-- SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai> -->
3
+ <!-- AI agents: do not edit this file without human approval. -->
4
+
5
+ # Dev
6
+
7
+ Roles:
8
+
9
+ - Analyst
10
+
11
+ The caller supplies a development request including any desired outcome, scope, and context.
12
+
13
+ `dev` is an optional repository-aware planner for a development request that needs more analysis before choosing a development path.
14
+ It coordinates existing playbooks and owns no repository commit itself.
15
+
16
+ At the start of `dev` and after each Boss reply, Captain shall relay the development request, relevant discussion context, and any relevant run results to Analyst in quotes (`>`), along with the following instruction:
17
+
18
+ ```markdown
19
+ Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.
20
+ Do not change files or commit while planning or discussing the request.
21
+
22
+ - If useful analysis or clarification should be discussed before any repository work, give Boss the useful response and ask one material question that advances the decision.
23
+ - If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.
24
+ - If implementation can proceed under the existing decisions, choose `code`.
25
+ - If implementation first requires a new or amended durable decision that the existing specs do not settle, choose `decide then code`.
26
+
27
+ A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.
28
+ Do not choose `decide then code` merely because the work is large.
29
+ Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.
30
+ ```
31
+
32
+ The planning result has four semantic outcomes: needs Boss reply, discussion complete, code, and decide then code.
33
+ Each outcome requires affirmative support in Analyst's result; absence of a reason to choose another outcome is not support.
34
+ No outcome depends on a fixed presentation format of Analyst's reply.
35
+ `dev` shall act on the accepted outcome itself and shall not return to the session Captain for another routing decision.
36
+
37
+ For needs Boss reply, `dev` shall use the standard Boss-question suspension with Analyst's complete response.
38
+ The session Captain shall present that response to Boss and, after Boss replies, resume `dev` with the question and answer in the same Analyst conversation.
39
+
40
+ Discussion complete is available only after a Boss reply, when any useful analysis has already been presented through needs Boss reply.
41
+ It completes `dev` without a child call or repository change.
42
+
43
+ For code, `dev` shall directly call playbook `code` with the development request, relevant discussion context, and planning result in quotes (`>`).
44
+
45
+ For decide then code, `dev` shall call playbook `decide` with the development request, relevant discussion context, and planning result in quotes (`>`).
46
+ Only after `decide` succeeds shall `dev` call playbook `code` with the development request, relevant discussion context, planning result, `decide`-owned commit, and exact evaluated repository revision in quotes (`>`).
47
+ `dev` shall not separately call `review` for the design scope already reviewed by `decide`.
48
+
49
+ `dev` completes with the successful result of its final child call.
50
+ If a child returns an authored abort or failure, or a terminal result that does not prove the success required for the selected path, `dev` shall start no later child and shall relay that canonical result.
51
+ If a child call fails outside its authored result contract, `dev` shall park as failed and retain the control-plane error.
52
+ `dev` shall consume commit identities only from each child's canonical structured result, never from player prose.