@sublang/playbook 13.3.0 → 14.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.
- package/README.md +5 -5
- package/docs/cli.md +14 -14
- package/docs/configuration.md +10 -2
- package/docs/embedding.md +20 -1
- package/package.json +6 -1
- package/reference/sdlc/branch.playbook/branch.fsm.d.ts +1 -1
- package/reference/sdlc/code.playbook/bin/launch-config.js +88 -5
- package/reference/sdlc/code.playbook/bin/playbook.js +4 -0
- package/reference/sdlc/code.playbook/bin/repository-effects.js +198 -17
- package/reference/sdlc/code.playbook/bin/run.js +4 -0
- package/reference/sdlc/code.playbook/bin/session-store.js +2 -2
- package/reference/sdlc/code.playbook/code.fsm.d.ts +1 -1
- package/reference/sdlc/code.playbook/host-capabilities.d.ts +6 -0
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +3 -1
- package/reference/sdlc/code.playbook/playbook-captain.js +374 -20
- package/reference/sdlc/code.playbook/playbook-captain.ts +478 -38
- package/reference/sdlc/code.playbook/session-host.d.ts +6 -3
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +1 -1
- package/reference/sdlc/decide.playbook/decide.playbook.js +25 -1
- package/reference/sdlc/decide.playbook/decide.playbook.ts +35 -2
- package/reference/sdlc/dev.md +15 -8
- package/reference/sdlc/dev.playbook/dev.fsm.d.ts +1 -1
- package/reference/sdlc/dev.playbook/dev.fsm.js +18 -12
- package/reference/sdlc/dev.playbook/dev.fsm.ts +18 -13
- package/reference/sdlc/dev.playbook/dev.gears.md +19 -12
- package/reference/sdlc/dev.playbook/dev.playbook.d.ts +3 -10
- package/reference/sdlc/dev.playbook/dev.playbook.js +6 -6
- package/reference/sdlc/dev.playbook/dev.playbook.ts +8 -9
- package/reference/sdlc/dev.playbook/dev.registry.js +1 -1
- package/reference/sdlc/dev.playbook/dev.registry.ts +1 -1
- package/reference/sdlc/pr.md +17 -4
- package/reference/sdlc/pr.playbook/pr.fsm.d.ts +18 -4
- package/reference/sdlc/pr.playbook/pr.fsm.js +49 -18
- package/reference/sdlc/pr.playbook/pr.fsm.ts +63 -20
- package/reference/sdlc/pr.playbook/pr.gears.md +20 -7
- package/reference/sdlc/pr.playbook/pr.playbook.js +2 -1
- package/reference/sdlc/pr.playbook/pr.playbook.ts +2 -1
- package/reference/sdlc/review.playbook/review.fsm.d.ts +1 -1
- package/slc/experiments/fsm-scaffold-guidance.md +31 -0
- package/slc/gears2fsm.md +145 -58
- package/slc/link.md +190 -7
- package/slc/materialize-link.mjs +460 -0
- package/slc/optimize.md +46 -6
- package/slc/scaffold-fsm.mjs +225 -0
- package/slc/slc.pin-inputs.json +13 -0
- package/slc/text2gears.md +38 -3
- package/slc/workflow-contracts.json +371 -0
- package/src/runtime.d.ts +70 -0
- package/src/runtime.js +232 -1
- package/src/runtime.ts +358 -1
- package/src/xstate-playbook-runtime.d.ts +9 -10
- package/src/xstate-playbook-runtime.js +407 -21
- package/src/xstate-playbook-runtime.ts +514 -20
- package/src/xstate-runtime.d.ts +9 -1
- package/src/xstate-runtime.js +299 -19
- package/src/xstate-runtime.ts +362 -21
|
@@ -72,7 +72,9 @@ export interface SessionHostController {
|
|
|
72
72
|
/**
|
|
73
73
|
* The active leaf's currently advertised runtime actions, read live between
|
|
74
74
|
* turns; empty whenever nothing is advertised, a turn is active, or the
|
|
75
|
-
* leaf's control view cannot be read.
|
|
75
|
+
* leaf's control view cannot be read. Each pair carries the leaf's own
|
|
76
|
+
* `standing` and its `reason`, so a host never draws a control without
|
|
77
|
+
* saying what running it would do (DR-063).
|
|
76
78
|
*/
|
|
77
79
|
listRuntimeActions(): readonly PlaybookControlAction[];
|
|
78
80
|
/**
|
|
@@ -83,8 +85,9 @@ export interface SessionHostController {
|
|
|
83
85
|
submitRuntimeAction(actionId: string): Promise<SessionRecovery>;
|
|
84
86
|
/**
|
|
85
87
|
* The controls the shell offers on its own behalf, read live between turns;
|
|
86
|
-
* today one give-up while a root is engaged, and empty while idle or
|
|
87
|
-
* turn is active. Disjoint from `listRuntimeActions`, which reads the
|
|
88
|
+
* today one `ready` give-up while a root is engaged, and empty while idle or
|
|
89
|
+
* while a turn is active. Disjoint from `listRuntimeActions`, which reads the
|
|
90
|
+
* leaf.
|
|
88
91
|
*/
|
|
89
92
|
listShellActions(): readonly PlaybookControlAction[];
|
|
90
93
|
/**
|
|
@@ -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" | "
|
|
217
|
+
}, never, "done" | "failed" | "ready" | "awaitBossReply" | "reportedReviewFailure" | "commitCoderProposal" | "reviewCommit" | {
|
|
218
218
|
independentProposals: {
|
|
219
219
|
coder: "complete" | "working" | "waiting";
|
|
220
220
|
reviewer: "complete" | "working" | "waiting";
|
|
@@ -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
|
-
...(
|
|
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
|
-
...(
|
|
3870
|
-
? { deferredRestoreOperationId: operation
|
|
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
|
},
|
package/reference/sdlc/dev.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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" | "
|
|
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
|
-
'
|
|
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
|
-
'
|
|
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
|
|
27
|
-
decideThenCode: 'Analyst
|
|
28
|
-
codeViaPullRequest: 'Analyst
|
|
29
|
-
decideThenCodeViaPullRequest: 'Analyst
|
|
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
|
|
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
|
-
'
|
|
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
|
-
'
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
>
|
|
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
|
-
>
|
|
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
|
|
39
|
-
- `decideThenCode`: Analyst
|
|
40
|
-
- `codeViaPullRequest`: Analyst
|
|
41
|
-
- `decideThenCodeViaPullRequest`: Analyst
|
|
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
|
|
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
|
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
import { type XStatePlaybookRuntimeFactory, type XStatePlaybookRuntimeConstruction } from '@sublang/playbook/xstate-runtime';
|
|
2
|
-
import { type DevInput
|
|
1
|
+
import { type PlaybookPlayerInput, type XStatePlaybookRuntimeFactory, type XStatePlaybookRuntimeConstruction } from '@sublang/playbook/xstate-runtime';
|
|
2
|
+
import { type DevInput } from './dev.fsm.js';
|
|
3
3
|
import type { PlaybookHostConstructionCapabilities } from '../code.playbook/playbook-captain.js';
|
|
4
4
|
import type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, PlayerResult, PlayerSessionStore } from '@sublang/playbook/runtime';
|
|
5
5
|
export type { CaptainCallOptions, CaptainResult, JsonValue, NormalizedError, PlayerCallOptions, PlaybookCallRequest, PlaybookCallResult, PlaybookCallStart, PlaybookControlReceipt, PlaybookControlView, PlaybookPendingCall, PlaybookPorts, PlaybookRunResult, PlaybookRuntime, PlaybookRuntimeFactory, PlaybookRuntimeSnapshot, PlaybookSession, PlaybookState, PlaybookStateValue, PlaybookTraceEvent, PlaybookTraceType, PlayerResult, PlayerSessionStore, };
|
|
6
6
|
export type DevPlaybookOptions = DevInput;
|
|
7
7
|
export type DevPlaybookHostCapabilities = PlaybookHostConstructionCapabilities & XStatePlaybookRuntimeConstruction<DevPlaybookOptions, object>['hostCapabilities'];
|
|
8
|
-
/**
|
|
9
|
-
* Preserve authored Markdown quote markers around every line of relayed
|
|
10
|
-
* runtime text. The generic composer preserves the marker itself; DEV's
|
|
11
|
-
* override additionally keeps a multiline value inside that quote and drops
|
|
12
|
-
* the optional relays that have no value yet.
|
|
13
|
-
*/
|
|
14
|
-
declare function composePlayerPrompt(input: PlayerInput, resuming?: boolean): string;
|
|
15
8
|
export declare const _internal: {
|
|
16
|
-
composePlayerPrompt:
|
|
9
|
+
composePlayerPrompt: (input: PlaybookPlayerInput, _identity: import("@sublang/playbook/xstate-runtime").XStatePromptIdentity, resuming?: boolean) => string;
|
|
17
10
|
VERBATIM_PAYLOAD_FIELDS: ReadonlySet<string>;
|
|
18
11
|
UNFINISHED_FINAL_STATE_IDS: ReadonlySet<string>;
|
|
19
12
|
};
|
|
@@ -73,11 +73,6 @@ function composePlayerPrompt(input, resuming = false) {
|
|
|
73
73
|
});
|
|
74
74
|
return composePlayerContinuation(input, body, resuming);
|
|
75
75
|
}
|
|
76
|
-
export const _internal = {
|
|
77
|
-
composePlayerPrompt,
|
|
78
|
-
VERBATIM_PAYLOAD_FIELDS,
|
|
79
|
-
UNFINISHED_FINAL_STATE_IDS,
|
|
80
|
-
};
|
|
81
76
|
const runtimeSpec = {
|
|
82
77
|
label: 'DEV',
|
|
83
78
|
// DR-022 / slc/link.md: the declaration carries the value current at link
|
|
@@ -97,7 +92,7 @@ const runtimeSpec = {
|
|
|
97
92
|
roleStates: {
|
|
98
93
|
planAnalysis: {
|
|
99
94
|
role: 'analyst',
|
|
100
|
-
label: 'Analyst is
|
|
95
|
+
label: 'Analyst is choosing the development path.',
|
|
101
96
|
},
|
|
102
97
|
},
|
|
103
98
|
// DR-044: DEV owns no repository commit — every Analyst outcome is
|
|
@@ -141,5 +136,10 @@ const runtimeSpec = {
|
|
|
141
136
|
unfinishedFinalStateIds: UNFINISHED_FINAL_STATE_IDS,
|
|
142
137
|
transitionEventFields: ['developmentRequest', 'answer', 'questionId'],
|
|
143
138
|
};
|
|
139
|
+
export const _internal = {
|
|
140
|
+
composePlayerPrompt: runtimeSpec.composePlayerPrompt,
|
|
141
|
+
VERBATIM_PAYLOAD_FIELDS,
|
|
142
|
+
UNFINISHED_FINAL_STATE_IDS,
|
|
143
|
+
};
|
|
144
144
|
const createPlaybookRuntime = createXStatePlaybookRuntime(devMachine, runtimeSpec);
|
|
145
145
|
export default createPlaybookRuntime;
|
|
@@ -160,12 +160,6 @@ function composePlayerPrompt(input: PlayerInput, resuming = false): string {
|
|
|
160
160
|
return composePlayerContinuation(input, body, resuming);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
export const _internal = {
|
|
164
|
-
composePlayerPrompt,
|
|
165
|
-
VERBATIM_PAYLOAD_FIELDS,
|
|
166
|
-
UNFINISHED_FINAL_STATE_IDS,
|
|
167
|
-
};
|
|
168
|
-
|
|
169
163
|
const runtimeSpec = {
|
|
170
164
|
label: 'DEV',
|
|
171
165
|
// DR-022 / slc/link.md: the declaration carries the value current at link
|
|
@@ -185,8 +179,7 @@ const runtimeSpec = {
|
|
|
185
179
|
roleStates: {
|
|
186
180
|
planAnalysis: {
|
|
187
181
|
role: 'analyst',
|
|
188
|
-
label:
|
|
189
|
-
'Analyst is analyzing the development request to choose the smallest sound next step.',
|
|
182
|
+
label: 'Analyst is choosing the development path.',
|
|
190
183
|
},
|
|
191
184
|
},
|
|
192
185
|
// DR-044: DEV owns no repository commit — every Analyst outcome is
|
|
@@ -224,7 +217,7 @@ const runtimeSpec = {
|
|
|
224
217
|
},
|
|
225
218
|
},
|
|
226
219
|
},
|
|
227
|
-
composePlayerPrompt: (input: PlaybookPlayerInput, _identity, resuming) =>
|
|
220
|
+
composePlayerPrompt: (input: PlaybookPlayerInput, _identity, resuming?: boolean) =>
|
|
228
221
|
composePlayerPrompt(input as PlayerInput, resuming),
|
|
229
222
|
verbatimPayloadFields: VERBATIM_PAYLOAD_FIELDS,
|
|
230
223
|
controlContextFields: [],
|
|
@@ -232,6 +225,12 @@ const runtimeSpec = {
|
|
|
232
225
|
transitionEventFields: ['developmentRequest', 'answer', 'questionId'],
|
|
233
226
|
} satisfies XStatePlaybookRuntimeSpecV3<DevPlaybookOptions>;
|
|
234
227
|
|
|
228
|
+
export const _internal = {
|
|
229
|
+
composePlayerPrompt: runtimeSpec.composePlayerPrompt,
|
|
230
|
+
VERBATIM_PAYLOAD_FIELDS,
|
|
231
|
+
UNFINISHED_FINAL_STATE_IDS,
|
|
232
|
+
};
|
|
233
|
+
|
|
235
234
|
const createPlaybookRuntime: XStatePlaybookRuntimeFactory<
|
|
236
235
|
XStatePlaybookRuntimeConstruction<
|
|
237
236
|
DevPlaybookOptions,
|
|
@@ -49,7 +49,7 @@ export function validateDevOptions(optionSlice) {
|
|
|
49
49
|
export const devPlaybookRegistryEntry = {
|
|
50
50
|
id: 'dev',
|
|
51
51
|
command: 'dev',
|
|
52
|
-
intent: '
|
|
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
|
-
'
|
|
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',
|
package/reference/sdlc/pr.md
CHANGED
|
@@ -19,7 +19,9 @@ The caller supplies:
|
|
|
19
19
|
`pr` delivers a reviewed branch into the repository default branch through a GitHub pull request: it publishes the branch, waits for the pull request's checks, fixes red checks through playbook `code` no more than once, and merges.
|
|
20
20
|
It changes no files and owns no repository commit; the one fix it may request is owned by `code`.
|
|
21
21
|
The check waits, the fix publication, the merge, and the local update are mechanical steps: each runs one fixed command whose exit status alone decides its two outcomes, reads no conversation, and produces no prose.
|
|
22
|
-
`gh` infers the pull request from the checked-out branch, so
|
|
22
|
+
`gh` infers the pull request from the checked-out branch, which the run does not own: the nested `code` call suspends across Boss turns, so the checkout can change before the fix is published or the merge runs.
|
|
23
|
+
The two steps that act on the pull request itself therefore carry one runtime value — the pull request `pr` published — and refuse unless the checkout still infers exactly it.
|
|
24
|
+
That identity is reported text, so it binds as a single-quoted shell literal: the command compares it as data and never executes it as shell syntax.
|
|
23
25
|
|
|
24
26
|
When the caller gives its input, Captain shall relay the complete caller input in quotes (`>`) to Coder, along with the following instruction:
|
|
25
27
|
|
|
@@ -66,6 +68,7 @@ When the nested `code` call fails outside that authored result contract, `pr` sh
|
|
|
66
68
|
|
|
67
69
|
When `code` succeeds, Captain shall publish the fix to the pull request by running exactly the following command in the repository, with no other action and without reading its output:
|
|
68
70
|
|
|
71
|
+
> [ "$(gh pr view --json url --jq .url)" = '<pull-request-url>' ] || exit 1
|
|
69
72
|
> git push || exit 1
|
|
70
73
|
> n=0
|
|
71
74
|
> until [ "$(gh pr view --json headRefOid --jq .headRefOid 2>/dev/null)" = "$(git rev-parse HEAD)" ]; do
|
|
@@ -75,6 +78,7 @@ When `code` succeeds, Captain shall publish the fix to the pull request by runni
|
|
|
75
78
|
> done
|
|
76
79
|
|
|
77
80
|
The publication has exactly two outcomes decided by the exit status alone: fix published on status zero, once the pull request's head is the pushed commit, and fix not published otherwise.
|
|
81
|
+
The command pushes nothing until the checked-out branch infers the pull request `pr` published, so a checkout that changed while `code` ran publishes to no other branch.
|
|
78
82
|
Fix not published is an authored failure that leaves the pull request open.
|
|
79
83
|
|
|
80
84
|
When the fix is published, Captain shall wait for the pull request's checks again by running exactly the same command as the first wait in the repository, with no other action and without reading its output:
|
|
@@ -92,11 +96,20 @@ Checks still failing is an authored failure that leaves the pull request open; t
|
|
|
92
96
|
|
|
93
97
|
When the checks pass, before or after the one fix attempt, Captain shall merge the pull request by running exactly the following command in the repository, with no other action and without reading its output:
|
|
94
98
|
|
|
95
|
-
> gh pr
|
|
99
|
+
> pr=$(gh pr view --json url --jq .url) || exit 1
|
|
100
|
+
> [ "$pr" = '<pull-request-url>' ] || exit 1
|
|
101
|
+
> base=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name) || exit 1
|
|
102
|
+
> [ -n "$base" ] || exit 1
|
|
103
|
+
> [ "$(gh pr view "$pr" --json baseRefName --jq .baseRefName)" = "$base" ] || exit 1
|
|
104
|
+
> gh pr merge --merge --delete-branch --match-head-commit "$(git rev-parse HEAD)" || exit 1
|
|
105
|
+
> [ "$(gh pr view "$pr" --json state --jq .state)" = MERGED ] || exit 1
|
|
106
|
+
> [ "$(git branch --show-current)" = "$base" ]
|
|
96
107
|
|
|
97
|
-
The merge creates a merge commit on the repository default branch,
|
|
108
|
+
The merge creates a merge commit on the repository default branch, requests deletion of the remote and local branch, and checks out the local default branch; GitHub closes the linked issue on merge.
|
|
98
109
|
The merge has exactly two outcomes decided by the exit status alone: merged on status zero and merge refused otherwise.
|
|
99
|
-
|
|
110
|
+
The command requires the inferred pull request to be the one `pr` published and to target the repository default branch before the irreversible merge, and confirms that same pull request's merged state and the default-branch checkout after the merge command succeeds; a queued pull request is not a merged result.
|
|
111
|
+
Merge refused is an authored failure that leaves the pull request in the state GitHub reports: the checkout no longer infers the published pull request, the pull request targets another branch, GitHub refused the merge for a conflict, a branch protection, a forbidden merge method, or a head that moved, or the merge may have landed while its confirmation, the local switch to the default branch, or the branch deletion failed.
|
|
112
|
+
A refused merge does not establish that the pull request is unmerged, so `pr` reports it as an unconfirmed merge rather than as not merged, and no outcome claims a branch was deleted, because `gh` skips the remote deletion for a pull request from another repository or one already merged and exits zero anyway.
|
|
100
113
|
|
|
101
114
|
When the pull request is merged, Captain shall bring the local default branch to the merged head by running exactly the following command in the repository, with no other action and without reading its output:
|
|
102
115
|
|