@sublang/playbook 14.0.0 → 15.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 (51) hide show
  1. package/README.md +1 -1
  2. package/docs/cli.md +14 -14
  3. package/docs/configuration.md +15 -9
  4. package/package.json +1 -1
  5. package/reference/sdlc/branch.playbook/branch.fsm.d.ts +1 -1
  6. package/reference/sdlc/code.md +1 -1
  7. package/reference/sdlc/code.playbook/bin/launch-config.js +55 -16
  8. package/reference/sdlc/code.playbook/bin/playbook.js +14 -9
  9. package/reference/sdlc/code.playbook/bin/repository-effects.js +187 -14
  10. package/reference/sdlc/code.playbook/bin/run.js +12 -8
  11. package/reference/sdlc/code.playbook/code.fsm.d.ts +1 -1
  12. package/reference/sdlc/code.playbook/code.fsm.js +1 -1
  13. package/reference/sdlc/code.playbook/code.fsm.ts +1 -1
  14. package/reference/sdlc/code.playbook/code.gears.md +2 -2
  15. package/reference/sdlc/code.playbook/host-capabilities.d.ts +6 -0
  16. package/reference/sdlc/code.playbook/playbook-captain.d.ts +3 -1
  17. package/reference/sdlc/code.playbook/playbook-captain.js +357 -16
  18. package/reference/sdlc/code.playbook/playbook-captain.ts +459 -34
  19. package/reference/sdlc/code.playbook/session-host.d.ts +6 -3
  20. package/reference/sdlc/decide.md +1 -1
  21. package/reference/sdlc/decide.playbook/decide.fsm.d.ts +1 -1
  22. package/reference/sdlc/decide.playbook/decide.fsm.js +1 -1
  23. package/reference/sdlc/decide.playbook/decide.fsm.ts +1 -1
  24. package/reference/sdlc/decide.playbook/decide.gears.md +1 -1
  25. package/reference/sdlc/decide.playbook/decide.playbook.js +25 -1
  26. package/reference/sdlc/decide.playbook/decide.playbook.ts +35 -2
  27. package/reference/sdlc/dev.md +15 -8
  28. package/reference/sdlc/dev.playbook/dev.fsm.d.ts +1 -1
  29. package/reference/sdlc/dev.playbook/dev.fsm.js +18 -12
  30. package/reference/sdlc/dev.playbook/dev.fsm.ts +18 -13
  31. package/reference/sdlc/dev.playbook/dev.gears.md +19 -12
  32. package/reference/sdlc/dev.playbook/dev.playbook.js +1 -1
  33. package/reference/sdlc/dev.playbook/dev.playbook.ts +1 -2
  34. package/reference/sdlc/dev.playbook/dev.registry.js +1 -1
  35. package/reference/sdlc/dev.playbook/dev.registry.ts +1 -1
  36. package/reference/sdlc/pr.playbook/pr.fsm.d.ts +1 -1
  37. package/reference/sdlc/review.md +1 -1
  38. package/reference/sdlc/review.playbook/review.fsm.d.ts +1 -1
  39. package/reference/sdlc/review.playbook/review.fsm.js +1 -1
  40. package/reference/sdlc/review.playbook/review.fsm.ts +1 -1
  41. package/reference/sdlc/review.playbook/review.gears.md +1 -1
  42. package/slc/link.md +32 -0
  43. package/src/runtime.d.ts +70 -0
  44. package/src/runtime.js +232 -1
  45. package/src/runtime.ts +358 -1
  46. package/src/xstate-playbook-runtime.d.ts +9 -10
  47. package/src/xstate-playbook-runtime.js +372 -20
  48. package/src/xstate-playbook-runtime.ts +460 -19
  49. package/src/xstate-runtime.d.ts +9 -1
  50. package/src/xstate-runtime.js +299 -19
  51. package/src/xstate-runtime.ts +362 -21
@@ -214,7 +214,7 @@ export declare const decideMachine: import("xstate").StateMachine<DecideContext,
214
214
  } | {
215
215
  type: "validReviewSuccess";
216
216
  params: unknown;
217
- }, never, "done" | "failed" | "awaitBossReply" | "ready" | "reportedReviewFailure" | "commitCoderProposal" | "reviewCommit" | {
217
+ }, never, "done" | "failed" | "ready" | "awaitBossReply" | "reportedReviewFailure" | "commitCoderProposal" | "reviewCommit" | {
218
218
  independentProposals: {
219
219
  coder: "complete" | "working" | "waiting";
220
220
  reviewer: "complete" | "working" | "waiting";
@@ -26,7 +26,7 @@ const SYNTHESIZE_COMMIT_PROMPT = [
26
26
  'Commit the result as one new commit, following @specs/packages/git.md.',
27
27
  'Make the commit message explain concisely what changed and why.',
28
28
  'Identify every new commit you make.',
29
- 'Coder is <coder-llm> and Reviewer is <reviewer-llm>.',
29
+ 'Credit every AI that contributed to this commit: Coder <coder-llm> and Reviewer <reviewer-llm>, whose proposal it carries.',
30
30
  '',
31
31
  '> Original topic: <caller-topic>',
32
32
  "> Reviewer's independent proposal: <reviewer-proposal>",
@@ -168,7 +168,7 @@ const SYNTHESIZE_COMMIT_PROMPT = [
168
168
  'Commit the result as one new commit, following @specs/packages/git.md.',
169
169
  'Make the commit message explain concisely what changed and why.',
170
170
  'Identify every new commit you make.',
171
- 'Coder is <coder-llm> and Reviewer is <reviewer-llm>.',
171
+ 'Credit every AI that contributed to this commit: Coder <coder-llm> and Reviewer <reviewer-llm>, whose proposal it carries.',
172
172
  '',
173
173
  '> Original topic: <caller-topic>',
174
174
  "> Reviewer's independent proposal: <reviewer-proposal>",
@@ -66,7 +66,7 @@ When both independent proposals are complete, Captain shall relay the complete t
66
66
  > Commit the result as one new commit, following @specs/packages/git.md.
67
67
  > Make the commit message explain concisely what changed and why.
68
68
  > Identify every new commit you make.
69
- > Coder is <coder-llm> and Reviewer is <reviewer-llm>.
69
+ > Credit every AI that contributed to this commit: Coder <coder-llm> and Reviewer <reviewer-llm>, whose proposal it carries.
70
70
  >
71
71
  > > Original topic: <caller-topic>
72
72
  > > Reviewer's independent proposal: <reviewer-proposal>
@@ -2481,20 +2481,42 @@ function createDecidePlaybookRuntime(options, deferredEffects) {
2481
2481
  }
2482
2482
  return 'restored';
2483
2483
  };
2484
+ /**
2485
+ * DR-063 §3: reconciliation re-reads the host's ledger, which the control
2486
+ * view has just done. With no eligible checkpoint restoration and a complete
2487
+ * receipt on every unresolved boundary, re-reading resolves nothing.
2488
+ */
2489
+ const reconciliationStanding = (restorationEligible) => {
2490
+ if (restorationEligible)
2491
+ return { standing: 'ready' };
2492
+ const envelopes = unresolvedEffectEnvelopeIdentities();
2493
+ if (envelopes.length === 0)
2494
+ return { standing: 'ready' };
2495
+ const boundaryIds = envelopes.flatMap((envelope) => envelope.kind === 'boundary'
2496
+ ? [envelope.boundaryId]
2497
+ : (effectLedgerMirror.logicalOperations.find(({ operationId }) => operationId === envelope.operationId)?.boundaryIds ?? []));
2498
+ if (boundaryIds.length === 0)
2499
+ return { standing: 'ready' };
2500
+ return boundaryIds.every((boundaryId) => effectLedgerMirror.boundaries.find((candidate) => candidate.boundaryId === boundaryId)?.physicalReceipt !== undefined)
2501
+ ? { standing: 'no-op', reason: 'receipt-complete' }
2502
+ : { standing: 'ready' };
2503
+ };
2484
2504
  const unresolvedControlCandidates = () => {
2485
2505
  if (!hasUnresolvedReconciliation())
2486
2506
  return [];
2487
2507
  const operation = hiddenDeferredOperationId === undefined
2488
2508
  ? undefined
2489
2509
  : effectLedgerMirror.logicalOperations.find(({ operationId }) => operationId === hiddenDeferredOperationId);
2510
+ const restorationEligible = operation?.checkpointRestorationEligible === true;
2490
2511
  return [
2491
2512
  {
2492
2513
  action: {
2493
2514
  id: UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID,
2494
2515
  label: 'Retry unresolved effect reconciliation',
2516
+ ...reconciliationStanding(restorationEligible),
2495
2517
  },
2496
2518
  kind: 'reconcile',
2497
- ...(operation?.checkpointRestorationEligible === true
2519
+ ...(restorationEligible
2498
2520
  ? { deferredRestoreOperationId: operation.operationId }
2499
2521
  : {}),
2500
2522
  },
@@ -2502,6 +2524,8 @@ function createDecidePlaybookRuntime(options, deferredEffects) {
2502
2524
  action: {
2503
2525
  id: UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID,
2504
2526
  label: 'Abandon unresolved workflow attempt',
2527
+ // DR-063 §3: abandonment always changes the session's standing.
2528
+ standing: 'ready',
2505
2529
  },
2506
2530
  kind: 'abandon',
2507
2531
  },
@@ -3850,6 +3850,35 @@ function createDecidePlaybookRuntime(
3850
3850
  };
3851
3851
  type UnresolvedControlCandidates = readonly UnresolvedControlCandidate[];
3852
3852
 
3853
+ /**
3854
+ * DR-063 §3: reconciliation re-reads the host's ledger, which the control
3855
+ * view has just done. With no eligible checkpoint restoration and a complete
3856
+ * receipt on every unresolved boundary, re-reading resolves nothing.
3857
+ */
3858
+ const reconciliationStanding = (
3859
+ restorationEligible: boolean,
3860
+ ): Pick<PlaybookControlAction, 'standing' | 'reason'> => {
3861
+ if (restorationEligible) return { standing: 'ready' };
3862
+ const envelopes = unresolvedEffectEnvelopeIdentities();
3863
+ if (envelopes.length === 0) return { standing: 'ready' };
3864
+ const boundaryIds = envelopes.flatMap((envelope) =>
3865
+ envelope.kind === 'boundary'
3866
+ ? [envelope.boundaryId]
3867
+ : (effectLedgerMirror.logicalOperations.find(
3868
+ ({ operationId }) => operationId === envelope.operationId,
3869
+ )?.boundaryIds ?? []),
3870
+ );
3871
+ if (boundaryIds.length === 0) return { standing: 'ready' };
3872
+ return boundaryIds.every(
3873
+ (boundaryId) =>
3874
+ effectLedgerMirror.boundaries.find(
3875
+ (candidate) => candidate.boundaryId === boundaryId,
3876
+ )?.physicalReceipt !== undefined,
3877
+ )
3878
+ ? { standing: 'no-op', reason: 'receipt-complete' }
3879
+ : { standing: 'ready' };
3880
+ };
3881
+
3853
3882
  const unresolvedControlCandidates = (): UnresolvedControlCandidates => {
3854
3883
  if (!hasUnresolvedReconciliation()) return [];
3855
3884
  const operation =
@@ -3859,21 +3888,25 @@ function createDecidePlaybookRuntime(
3859
3888
  ({ operationId }) =>
3860
3889
  operationId === hiddenDeferredOperationId,
3861
3890
  );
3891
+ const restorationEligible = operation?.checkpointRestorationEligible === true;
3862
3892
  return [
3863
3893
  {
3864
3894
  action: {
3865
3895
  id: UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID,
3866
3896
  label: 'Retry unresolved effect reconciliation',
3897
+ ...reconciliationStanding(restorationEligible),
3867
3898
  },
3868
3899
  kind: 'reconcile',
3869
- ...(operation?.checkpointRestorationEligible === true
3870
- ? { deferredRestoreOperationId: operation.operationId }
3900
+ ...(restorationEligible
3901
+ ? { deferredRestoreOperationId: operation!.operationId }
3871
3902
  : {}),
3872
3903
  },
3873
3904
  {
3874
3905
  action: {
3875
3906
  id: UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID,
3876
3907
  label: 'Abandon unresolved workflow attempt',
3908
+ // DR-063 §3: abandonment always changes the session's standing.
3909
+ standing: 'ready',
3877
3910
  },
3878
3911
  kind: 'abandon',
3879
3912
  },
@@ -12,6 +12,7 @@ The caller supplies a development request including any desired outcome, scope,
12
12
 
13
13
  `dev` is an optional repository-aware planner for a development request that needs more analysis before choosing a development path.
14
14
  It coordinates existing playbooks and owns no repository commit itself.
15
+ Analyst chooses the path and states why in a short planning note; analysis, design, and implementation belong to the playbooks `dev` calls, and a question to Boss serves only the choice of path.
15
16
 
16
17
  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
 
@@ -20,17 +21,23 @@ At the start of `dev` and after each Boss reply, Captain shall relay the develop
20
21
  > Run results: <run-results>
21
22
 
22
23
  ```markdown
23
- Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.
24
+ Plan which playbooks run for this request; the playbooks do the work.
25
+ Read the request, then the specs and the repository only as far as choosing the path requires.
24
26
  Do not change files or commit while planning or discussing the request.
25
27
 
26
- - 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.
27
- - If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.
28
- - If implementation can proceed under the existing decisions, choose `code`.
29
- - If implementation first requires a new or amended durable decision that the existing specs do not settle, choose `decide then code`.
30
- - If the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery, read the issue and its comments as part of the analysis (`gh issue view --comments` with the issue number) and choose `code via pull request` or `decide then code via pull request` in place of `code` or `decide then code`.
28
+ Choose exactly one:
31
29
 
30
+ - `code`: the existing decisions and spec items settle how the work is done.
31
+ - `decide then code`: the work turns on a rule, concept, term, shape, or trade-off the specs leave open or contradict; name what is open, do not settle it.
32
+ - `code via pull request` or `decide then code via pull request`, in place of the two above, when the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery; read the issue and its comments (`gh issue view --comments` with the issue number) while choosing.
33
+ - A question to Boss, only when the answer would change which path runs or whether any work is wanted: one short question naming the alternatives it decides between, and nothing else. When every answer leads to the same path, choose it; the playbook settles the open point.
34
+ - `discussion complete`, after a Boss reply, when no repository work should follow.
35
+
36
+ Your reply is the planning note the chosen playbook receives. It holds only the path and, in at most ten lines, why — the decisions and spec items that settle the work, or the open point a decision must settle — plus the scope the request implies and any fact from the issue the playbooks need.
37
+ It holds no design, proposal, implementation instruction, or file-level finding: the playbooks own those.
38
+ A reply that chooses a path asks Boss nothing; a reply that asks Boss chooses no path.
32
39
  A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.
33
- Do not choose `decide then code` merely because the work is large.
40
+ Do not choose `decide then code` merely because the work is large, nor `code` merely because it is small.
34
41
  Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.
35
42
  ```
36
43
 
@@ -42,7 +49,7 @@ No outcome depends on a fixed presentation format of Analyst's reply.
42
49
  For needs Boss reply, `dev` shall use the standard Boss-question suspension with Analyst's complete response.
43
50
  The session Captain shall present that response to Boss and, after Boss replies, resume `dev` with the answer in the same Analyst conversation; include the previous question only when that conversation must start fresh.
44
51
 
45
- Discussion complete is available only after a Boss reply, when any useful analysis has already been presented through needs Boss reply.
52
+ Discussion complete is available only after a Boss reply, when that reply settles that no repository work should follow.
46
53
  It completes `dev` without a child call or repository change.
47
54
 
48
55
  For code, `dev` shall directly call playbook `code` with the development request, relevant discussion context, and planning result in quotes (`>`).
@@ -296,7 +296,7 @@ export declare const devMachine: import("xstate").StateMachine<DevContext, {
296
296
  } | {
297
297
  type: "resumesPlanAnalysis";
298
298
  params: unknown;
299
- }, never, "done" | "failed" | "awaitBossReply" | "ready" | "planAnalysis" | "createBranch" | "callCode" | "callDecide" | "callCodeAfterDecide" | "openPullRequest" | "discussionComplete" | "reportedChildFailure", string, DevInput, {
299
+ }, never, "done" | "failed" | "ready" | "awaitBossReply" | "planAnalysis" | "createBranch" | "callCode" | "callDecide" | "callCodeAfterDecide" | "openPullRequest" | "discussionComplete" | "reportedChildFailure", string, DevInput, {
300
300
  readonly status: "discussion-complete";
301
301
  } | {
302
302
  readonly status: "complete";
@@ -8,30 +8,36 @@ const PLAN_ANALYSIS_PROMPT = [
8
8
  '> Prior discussion: <discussion-context>',
9
9
  '> Run results: <run-results>',
10
10
  '',
11
- 'Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.',
11
+ 'Plan which playbooks run for this request; the playbooks do the work.',
12
+ 'Read the request, then the specs and the repository only as far as choosing the path requires.',
12
13
  'Do not change files or commit while planning or discussing the request.',
13
14
  '',
14
- '- 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.',
15
- '- If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.',
16
- '- If implementation can proceed under the existing decisions, choose `code`.',
17
- '- If implementation first requires a new or amended durable decision that the existing specs do not settle, choose `decide then code`.',
18
- '- If the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery, read the issue and its comments as part of the analysis (`gh issue view --comments` with the issue number) and choose `code via pull request` or `decide then code via pull request` in place of `code` or `decide then code`.',
15
+ 'Choose exactly one:',
19
16
  '',
17
+ '- `code`: the existing decisions and spec items settle how the work is done.',
18
+ '- `decide then code`: the work turns on a rule, concept, term, shape, or trade-off the specs leave open or contradict; name what is open, do not settle it.',
19
+ '- `code via pull request` or `decide then code via pull request`, in place of the two above, when the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery; read the issue and its comments (`gh issue view --comments` with the issue number) while choosing.',
20
+ '- A question to Boss, only when the answer would change which path runs or whether any work is wanted: one short question naming the alternatives it decides between, and nothing else. When every answer leads to the same path, choose it; the playbook settles the open point.',
21
+ '- `discussion complete`, after a Boss reply, when no repository work should follow.',
22
+ '',
23
+ 'Your reply is the planning note the chosen playbook receives. It holds only the path and, in at most ten lines, why — the decisions and spec items that settle the work, or the open point a decision must settle — plus the scope the request implies and any fact from the issue the playbooks need.',
24
+ 'It holds no design, proposal, implementation instruction, or file-level finding: the playbooks own those.',
25
+ 'A reply that chooses a path asks Boss nothing; a reply that asks Boss chooses no path.',
20
26
  'A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.',
21
- 'Do not choose `decide then code` merely because the work is large.',
27
+ 'Do not choose `decide then code` merely because the work is large, nor `code` merely because it is small.',
22
28
  'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
23
29
  ].join('\n');
24
30
  const PLAN_ANALYSIS_RESULTS = {
25
31
  discussionComplete: 'Analyst concluded the discussion after a Boss reply, with no repository work to follow.',
26
- code: 'Analyst determined implementation can proceed under the existing decisions. Output shall include `planningResult: <verbatim final text>`.',
27
- decideThenCode: 'Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle. Output shall include `planningResult: <verbatim final text>`.',
28
- codeViaPullRequest: 'Analyst determined implementation can proceed under the existing decisions and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
29
- decideThenCodeViaPullRequest: 'Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
32
+ code: 'Analyst chose `code`: the existing decisions and spec items settle how the work is done. Output shall include `planningResult: <verbatim final text>`.',
33
+ decideThenCode: 'Analyst chose `decide then code`: the work turns on a point the specs leave open or contradict. Output shall include `planningResult: <verbatim final text>`.',
34
+ codeViaPullRequest: 'Analyst chose `code via pull request`: the existing decisions settle the work, and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
35
+ decideThenCodeViaPullRequest: 'Analyst chose `decide then code via pull request`: the work turns on a point the specs leave open or contradict, and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
30
36
  needsBossReply: "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.",
31
37
  };
32
38
  const STATE_DESCRIPTIONS = {
33
39
  ready: 'Waiting for a development request.',
34
- planAnalysis: 'Analyst is analyzing the development request to choose the smallest sound next step.',
40
+ planAnalysis: 'Analyst is choosing the development path.',
35
41
  createBranch: 'The BRANCH playbook is preparing the pull-request branch for the planned development path.',
36
42
  callCode: 'The CODE playbook is implementing the planned development path.',
37
43
  callDecide: 'The DECIDE playbook is settling the durable decision the planned path requires.',
@@ -198,17 +198,23 @@ const PLAN_ANALYSIS_PROMPT = [
198
198
  '> Prior discussion: <discussion-context>',
199
199
  '> Run results: <run-results>',
200
200
  '',
201
- 'Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.',
201
+ 'Plan which playbooks run for this request; the playbooks do the work.',
202
+ 'Read the request, then the specs and the repository only as far as choosing the path requires.',
202
203
  'Do not change files or commit while planning or discussing the request.',
203
204
  '',
204
- '- 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.',
205
- '- If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.',
206
- '- If implementation can proceed under the existing decisions, choose `code`.',
207
- '- If implementation first requires a new or amended durable decision that the existing specs do not settle, choose `decide then code`.',
208
- '- If the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery, read the issue and its comments as part of the analysis (`gh issue view --comments` with the issue number) and choose `code via pull request` or `decide then code via pull request` in place of `code` or `decide then code`.',
205
+ 'Choose exactly one:',
209
206
  '',
207
+ '- `code`: the existing decisions and spec items settle how the work is done.',
208
+ '- `decide then code`: the work turns on a rule, concept, term, shape, or trade-off the specs leave open or contradict; name what is open, do not settle it.',
209
+ '- `code via pull request` or `decide then code via pull request`, in place of the two above, when the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery; read the issue and its comments (`gh issue view --comments` with the issue number) while choosing.',
210
+ '- A question to Boss, only when the answer would change which path runs or whether any work is wanted: one short question naming the alternatives it decides between, and nothing else. When every answer leads to the same path, choose it; the playbook settles the open point.',
211
+ '- `discussion complete`, after a Boss reply, when no repository work should follow.',
212
+ '',
213
+ 'Your reply is the planning note the chosen playbook receives. It holds only the path and, in at most ten lines, why — the decisions and spec items that settle the work, or the open point a decision must settle — plus the scope the request implies and any fact from the issue the playbooks need.',
214
+ 'It holds no design, proposal, implementation instruction, or file-level finding: the playbooks own those.',
215
+ 'A reply that chooses a path asks Boss nothing; a reply that asks Boss chooses no path.',
210
216
  'A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.',
211
- 'Do not choose `decide then code` merely because the work is large.',
217
+ 'Do not choose `decide then code` merely because the work is large, nor `code` merely because it is small.',
212
218
  'Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.',
213
219
  ].join('\n');
214
220
 
@@ -216,21 +222,20 @@ const PLAN_ANALYSIS_RESULTS = {
216
222
  discussionComplete:
217
223
  'Analyst concluded the discussion after a Boss reply, with no repository work to follow.',
218
224
  code:
219
- 'Analyst determined implementation can proceed under the existing decisions. Output shall include `planningResult: <verbatim final text>`.',
225
+ 'Analyst chose `code`: the existing decisions and spec items settle how the work is done. Output shall include `planningResult: <verbatim final text>`.',
220
226
  decideThenCode:
221
- 'Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle. Output shall include `planningResult: <verbatim final text>`.',
227
+ 'Analyst chose `decide then code`: the work turns on a point the specs leave open or contradict. Output shall include `planningResult: <verbatim final text>`.',
222
228
  codeViaPullRequest:
223
- 'Analyst determined implementation can proceed under the existing decisions and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
229
+ 'Analyst chose `code via pull request`: the existing decisions settle the work, and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
224
230
  decideThenCodeViaPullRequest:
225
- 'Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
231
+ 'Analyst chose `decide then code via pull request`: the work turns on a point the specs leave open or contradict, and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.',
226
232
  needsBossReply:
227
233
  "The acting agent's prose surfaces a clarifying question for Boss that the agent cannot answer alone. Output shall include `question: <verbatim question text from the acting agent's prose>`.",
228
234
  } as const;
229
235
 
230
236
  const STATE_DESCRIPTIONS = {
231
237
  ready: 'Waiting for a development request.',
232
- planAnalysis:
233
- 'Analyst is analyzing the development request to choose the smallest sound next step.',
238
+ planAnalysis: 'Analyst is choosing the development path.',
234
239
  createBranch:
235
240
  'The BRANCH playbook is preparing the pull-request branch for the planned development path.',
236
241
  callCode: 'The CODE playbook is implementing the planned development path.',
@@ -9,6 +9,7 @@ Roles:
9
9
 
10
10
  `dev` is an optional repository-aware planner for a development request that needs more analysis before choosing a development path.
11
11
  It coordinates existing playbooks and owns no repository commit itself.
12
+ Analyst chooses the path and states why in a short planning note; analysis, design, and implementation belong to the playbooks `dev` calls, and a question to Boss serves only the choice of path.
12
13
 
13
14
  ## Analyst
14
15
 
@@ -20,31 +21,37 @@ At the start of `dev` and after each Boss reply, Captain shall relay the develop
20
21
  > > Prior discussion: <discussion-context>
21
22
  > > Run results: <run-results>
22
23
  >
23
- > Inspect the request and the relevant repository and specs only as needed to determine the smallest sound next step.
24
+ > Plan which playbooks run for this request; the playbooks do the work.
25
+ > Read the request, then the specs and the repository only as far as choosing the path requires.
24
26
  > Do not change files or commit while planning or discussing the request.
25
27
  >
26
- > - 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.
27
- > - If the discussion has concluded after a Boss reply and no repository work should follow, choose `discussion complete`.
28
- > - If implementation can proceed under the existing decisions, choose `code`.
29
- > - If implementation first requires a new or amended durable decision that the existing specs do not settle, choose `decide then code`.
30
- > - If the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery, read the issue and its comments as part of the analysis (`gh issue view --comments` with the issue number) and choose `code via pull request` or `decide then code via pull request` in place of `code` or `decide then code`.
28
+ > Choose exactly one:
31
29
  >
30
+ > - `code`: the existing decisions and spec items settle how the work is done.
31
+ > - `decide then code`: the work turns on a rule, concept, term, shape, or trade-off the specs leave open or contradict; name what is open, do not settle it.
32
+ > - `code via pull request` or `decide then code via pull request`, in place of the two above, when the request names a GitHub issue (number or URL) or explicitly asks for pull-request delivery; read the issue and its comments (`gh issue view --comments` with the issue number) while choosing.
33
+ > - A question to Boss, only when the answer would change which path runs or whether any work is wanted: one short question naming the alternatives it decides between, and nothing else. When every answer leads to the same path, choose it; the playbook settles the open point.
34
+ > - `discussion complete`, after a Boss reply, when no repository work should follow.
35
+ >
36
+ > Your reply is the planning note the chosen playbook receives. It holds only the path and, in at most ten lines, why — the decisions and spec items that settle the work, or the open point a decision must settle — plus the scope the request implies and any fact from the issue the playbooks need.
37
+ > It holds no design, proposal, implementation instruction, or file-level finding: the playbooks own those.
38
+ > A reply that chooses a path asks Boss nothing; a reply that asks Boss chooses no path.
32
39
  > A question or exploratory discussion is not by itself authorization to create a durable decision or implement changes.
33
- > Do not choose `decide then code` merely because the work is large.
40
+ > Do not choose `decide then code` merely because the work is large, nor `code` merely because it is small.
34
41
  > Consult @specs/map.md for relevant context and @specs/meta.md for spec requirements, if needed.
35
42
 
36
43
  Results:
37
44
  - `discussionComplete`: Analyst concluded the discussion after a Boss reply, with no repository work to follow.
38
- - `code`: Analyst determined implementation can proceed under the existing decisions. Output shall include `planningResult: <verbatim final text>`.
39
- - `decideThenCode`: Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle. Output shall include `planningResult: <verbatim final text>`.
40
- - `codeViaPullRequest`: Analyst determined implementation can proceed under the existing decisions and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.
41
- - `decideThenCodeViaPullRequest`: Analyst determined implementation first requires a new or amended durable decision that the existing specs do not settle and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.
45
+ - `code`: Analyst chose `code`: the existing decisions and spec items settle how the work is done. Output shall include `planningResult: <verbatim final text>`.
46
+ - `decideThenCode`: Analyst chose `decide then code`: the work turns on a point the specs leave open or contradict. Output shall include `planningResult: <verbatim final text>`.
47
+ - `codeViaPullRequest`: Analyst chose `code via pull request`: the existing decisions settle the work, and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.
48
+ - `decideThenCodeViaPullRequest`: Analyst chose `decide then code via pull request`: the work turns on a point the specs leave open or contradict, and the request names a GitHub issue or explicitly asks for pull-request delivery. Output shall include `planningResult: <verbatim final text>`.
42
49
 
43
50
  Workflow outcomes:
44
51
  - The planning result has six semantic outcomes: needs Boss reply, discussion complete, code, decide then code, code via pull request, and decide then code via pull request.
45
52
  - Each outcome requires affirmative support in Analyst's result; absence of a reason to choose another outcome is not support, and no outcome depends on a fixed presentation format of Analyst's reply.
46
53
  - Needs Boss reply uses the standard Boss-question suspension with Analyst's complete response; after Boss replies, `dev` resumes with the answer in the same Analyst conversation; the previous question is included only when that conversation must start fresh.
47
- - Discussion complete is available only after a Boss reply, when any useful analysis has already been presented through needs Boss reply; it completes `dev` without a child call or repository change.
54
+ - Discussion complete is available only after a Boss reply, when that reply settles that no repository work should follow; it completes `dev` without a child call or repository change.
48
55
  - `dev` acts on the accepted outcome itself and does not return to the session Captain for another routing decision.
49
56
 
50
57
  ## Nested development paths
@@ -92,7 +92,7 @@ const runtimeSpec = {
92
92
  roleStates: {
93
93
  planAnalysis: {
94
94
  role: 'analyst',
95
- label: 'Analyst is analyzing the development request to choose the smallest sound next step.',
95
+ label: 'Analyst is choosing the development path.',
96
96
  },
97
97
  },
98
98
  // DR-044: DEV owns no repository commit — every Analyst outcome is
@@ -179,8 +179,7 @@ const runtimeSpec = {
179
179
  roleStates: {
180
180
  planAnalysis: {
181
181
  role: 'analyst',
182
- label:
183
- 'Analyst is analyzing the development request to choose the smallest sound next step.',
182
+ label: 'Analyst is choosing the development path.',
184
183
  },
185
184
  },
186
185
  // DR-044: DEV owns no repository commit — every Analyst outcome is
@@ -49,7 +49,7 @@ export function validateDevOptions(optionSlice) {
49
49
  export const devPlaybookRegistryEntry = {
50
50
  id: 'dev',
51
51
  command: 'dev',
52
- intent: 'analyze a development request that needs planning before choosing direct implementation or a durable decision first, delivered through a pull request when the request names a GitHub issue or asks for one',
52
+ intent: 'plan the development path for a request: direct implementation, or a durable decision first, delivered through a pull request when the request names a GitHub issue or asks for one',
53
53
  artifactSchema: 3,
54
54
  runtimeProfile: Object.freeze({
55
55
  kind: 'shared-factory',
@@ -104,7 +104,7 @@ export const devPlaybookRegistryEntry: DevPlaybookRegistryEntry = {
104
104
  id: 'dev',
105
105
  command: 'dev',
106
106
  intent:
107
- 'analyze a development request that needs planning before choosing direct implementation or a durable decision first, delivered through a pull request when the request names a GitHub issue or asks for one',
107
+ 'plan the development path for a request: direct implementation, or a durable decision first, delivered through a pull request when the request names a GitHub issue or asks for one',
108
108
  artifactSchema: 3,
109
109
  runtimeProfile: Object.freeze({
110
110
  kind: 'shared-factory',
@@ -278,7 +278,7 @@ export declare const prMachine: import("xstate").StateMachine<PrContext, {
278
278
  } | {
279
279
  type: "resumesOpenPullRequest";
280
280
  params: unknown;
281
- }, never, "failed" | "awaitBossReply" | "ready" | "openPullRequest" | "waitForChecks" | "fixChecks" | "publishFix" | "waitForChecksAfterFix" | "mergePullRequest" | "updateLocalDefault" | "notPublished" | "fixNotPublished" | "checksStillFailing" | "merged" | "mergeRefused" | "mergedLocalBehind" | "fixFailed", string, Readonly<Record<never, never>>, {
281
+ }, never, "failed" | "ready" | "awaitBossReply" | "openPullRequest" | "waitForChecks" | "fixChecks" | "publishFix" | "waitForChecksAfterFix" | "mergePullRequest" | "updateLocalDefault" | "notPublished" | "fixNotPublished" | "checksStillFailing" | "merged" | "mergeRefused" | "mergedLocalBehind" | "fixFailed", string, Readonly<Record<never, never>>, {
282
282
  readonly status: "merged";
283
283
  /** The pull request number, exactly as Coder reported it. */
284
284
  readonly pullRequest: string;
@@ -101,7 +101,7 @@ If you accept any item, make minimal changes and add one new review-fix commit;
101
101
  Follow @specs/packages/git.md.
102
102
  Make the commit message explain concisely what changed and why, including relevant verification.
103
103
  Identify every new commit you make.
104
- Coder is <coder-llm>; Reviewer is <reviewer-llm>.
104
+ Credit every AI that contributed to this commit: Coder <coder-llm> and Reviewer <reviewer-llm>, whose findings it answers.
105
105
 
106
106
  If you reject every item, change nothing and make no commit.
107
107
  Report every disposition, all relevant run results, and every rebuttal.
@@ -169,7 +169,7 @@ export declare const reviewMachine: import("xstate").StateMachine<ReviewContext,
169
169
  } | {
170
170
  type: "restartInitialReview";
171
171
  params: unknown;
172
- }, never, "done" | "failed" | "awaitBossReply" | "ready" | "reviewInitial" | "addressFindings" | "reviewAfterCommit" | "reviewAfterRebuttal", string, Readonly<Record<string, never>>, ReviewOutput, import("xstate").EventObject, import("xstate").MetaObject, {
172
+ }, never, "done" | "failed" | "ready" | "awaitBossReply" | "reviewInitial" | "addressFindings" | "reviewAfterCommit" | "reviewAfterRebuttal", string, Readonly<Record<string, never>>, ReviewOutput, import("xstate").EventObject, import("xstate").MetaObject, {
173
173
  id: "review";
174
174
  states: {
175
175
  readonly ready: {
@@ -67,7 +67,7 @@ const CODER_DISPOSITION_PROMPT = [
67
67
  'Follow @specs/packages/git.md.',
68
68
  'Make the commit message explain concisely what changed and why, including relevant verification.',
69
69
  'Identify every new commit you make.',
70
- 'Coder is <coder-llm>; Reviewer is <reviewer-llm>.',
70
+ 'Credit every AI that contributed to this commit: Coder <coder-llm> and Reviewer <reviewer-llm>, whose findings it answers.',
71
71
  '',
72
72
  'If you reject every item, change nothing and make no commit.',
73
73
  'Report every disposition, all relevant run results, and every rebuttal.',
@@ -156,7 +156,7 @@ const CODER_DISPOSITION_PROMPT = [
156
156
  'Follow @specs/packages/git.md.',
157
157
  'Make the commit message explain concisely what changed and why, including relevant verification.',
158
158
  'Identify every new commit you make.',
159
- 'Coder is <coder-llm>; Reviewer is <reviewer-llm>.',
159
+ 'Credit every AI that contributed to this commit: Coder <coder-llm> and Reviewer <reviewer-llm>, whose findings it answers.',
160
160
  '',
161
161
  'If you reject every item, change nothing and make no commit.',
162
162
  'Report every disposition, all relevant run results, and every rebuttal.',
@@ -65,7 +65,7 @@ When Reviewer raises or keeps any finding, Captain shall relay the caller input
65
65
  > Follow @specs/packages/git.md.
66
66
  > Make the commit message explain concisely what changed and why, including relevant verification.
67
67
  > Identify every new commit you make.
68
- > Coder is <coder-llm>; Reviewer is <reviewer-llm>.
68
+ > Credit every AI that contributed to this commit: Coder <coder-llm> and Reviewer <reviewer-llm>, whose findings it answers.
69
69
  >
70
70
  > If you reject every item, change nothing and make no commit.
71
71
  > Report every disposition, all relevant run results, and every rebuttal.
package/slc/link.md CHANGED
@@ -247,10 +247,36 @@ type JsonValue =
247
247
  | readonly JsonValue[]
248
248
  | { readonly [key: string]: JsonValue };
249
249
 
250
+ // DR-063: the closed failure-cause contract. `code` is one of
251
+ // PLAYBOOK_FAILURE_CODES and `evidence` holds exactly the members that code
252
+ // names — repository paths, dispositions, classifications, and revisions, never
253
+ // content or internal identity.
254
+ type PlaybookFailureCode =
255
+ | 'commit-missing'
256
+ | 'commit-residual'
257
+ | 'pre-existing-lost'
258
+ | 'commits-more-than-one'
259
+ | 'history-rewritten'
260
+ | 'foreign-change'
261
+ | 'observation-unstable'
262
+ | 'attribution-ambiguous'
263
+ | 'receipt-missing'
264
+ | 'judge-failed'
265
+ | 'player-failed'
266
+ | 'aborted'
267
+ | 'child-failed'
268
+ | 'runtime-defect';
269
+
270
+ interface PlaybookFailureCause {
271
+ readonly code: PlaybookFailureCode;
272
+ readonly evidence: PlaybookFailureEvidence;
273
+ }
274
+
250
275
  interface NormalizedError {
251
276
  name: string;
252
277
  message: string;
253
278
  stack?: string;
279
+ cause?: PlaybookFailureCause; // present when the error carried a valid one
254
280
  }
255
281
 
256
282
  type PlaybookStateValue =
@@ -2045,9 +2071,15 @@ parked-session snapshot capability; the pair changes no runtime ABI and no
2045
2071
  artifact or snapshot schema.
2046
2072
 
2047
2073
  ```typescript
2074
+ type PlaybookControlStanding = 'ready' | 'no-op' | 'blocked';
2075
+ type PlaybookControlActionReason = 'receipt-complete';
2076
+
2048
2077
  interface PlaybookControlAction {
2049
2078
  id: string; // stable within the returned view
2050
2079
  label: string; // runtime-written, Boss-appropriate
2080
+ standing?: PlaybookControlStanding; // what running it would do (DR-063);
2081
+ // absent reads as `ready`
2082
+ reason?: PlaybookControlActionReason; // why it is not `ready`
2051
2083
  }
2052
2084
 
2053
2085
  interface PlaybookControlView {