@sublang/playbook 0.1.3 → 0.2.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 +23 -17
- package/package.json +22 -22
- package/{code.fsm.d.ts → reference/sdlc/code.playbook/code.fsm.d.ts} +18 -0
- package/{code.fsm.introspect.d.ts → reference/sdlc/code.playbook/code.fsm.introspect.d.ts} +24 -0
- package/{code.fsm.introspect.js → reference/sdlc/code.playbook/code.fsm.introspect.js} +28 -1
- package/{code.fsm.introspect.ts → reference/sdlc/code.playbook/code.fsm.introspect.ts} +73 -6
- package/{code.fsm.js → reference/sdlc/code.playbook/code.fsm.js} +252 -103
- package/{code.fsm.ts → reference/sdlc/code.playbook/code.fsm.ts} +315 -107
- package/{code.gears.md → reference/sdlc/code.playbook/code.gears.md} +4 -3
- package/{code.playbook.d.ts → reference/sdlc/code.playbook/code.playbook.d.ts} +14 -2
- package/{code.playbook.js → reference/sdlc/code.playbook/code.playbook.js} +172 -79
- package/{code.playbook.ts → reference/sdlc/code.playbook/code.playbook.ts} +263 -72
- package/{tmux-play.config.yaml → reference/sdlc/code.playbook/tmux-play.config.yaml} +8 -0
- package/{tmux-play.production.config.yaml → reference/sdlc/code.playbook/tmux-play.production.config.yaml} +6 -0
- /package/{bin → reference/sdlc/code.playbook/bin}/playbook-code.js +0 -0
- /package/{code.tmux-play.d.ts → reference/sdlc/code.playbook/code.tmux-play.d.ts} +0 -0
- /package/{code.tmux-play.js → reference/sdlc/code.playbook/code.tmux-play.js} +0 -0
- /package/{code.tmux-play.ts → reference/sdlc/code.playbook/code.tmux-play.ts} +0 -0
package/README.md
CHANGED
|
@@ -34,9 +34,12 @@ committer loop end to end.
|
|
|
34
34
|
|
|
35
35
|
The reference is the canonical worked example —
|
|
36
36
|
[CODE source](reference/sdlc/code.md) →
|
|
37
|
-
[gears](code.gears.md) →
|
|
38
|
-
[FSM](code.fsm.ts) → runtime — with
|
|
39
|
-
runtime ported to cligent's `tmux-play` host out of the box.
|
|
37
|
+
[gears](reference/sdlc/code.playbook/code.gears.md) →
|
|
38
|
+
[FSM](reference/sdlc/code.playbook/code.fsm.ts) → runtime — with
|
|
39
|
+
the runtime ported to cligent's `tmux-play` host out of the box.
|
|
40
|
+
The compiled artifacts live under
|
|
41
|
+
[`reference/sdlc/code.playbook/`](reference/sdlc/code.playbook),
|
|
42
|
+
the slc pipeline's `<basename>.<pipeline>/` output directory.
|
|
40
43
|
|
|
41
44
|
### Install (users)
|
|
42
45
|
|
|
@@ -53,6 +56,9 @@ Each adapter reads its own auth: the Claude SDK uses your local
|
|
|
53
56
|
Claude Code auth (or `ANTHROPIC_API_KEY`); the Codex SDK uses your
|
|
54
57
|
local codex CLI auth (or `OPENAI_API_KEY`).
|
|
55
58
|
|
|
59
|
+
The bundled configs run each agent in cligent's protected auto mode
|
|
60
|
+
(`permissions.mode: auto`), suppressing routine approval prompts.
|
|
61
|
+
|
|
56
62
|
Then launch the reference playbook in a `tmux-play` session:
|
|
57
63
|
|
|
58
64
|
```sh
|
|
@@ -77,7 +83,7 @@ pnpm build
|
|
|
77
83
|
pnpm test
|
|
78
84
|
```
|
|
79
85
|
|
|
80
|
-
`pnpm install` resolves `@sublang/cligent` (
|
|
86
|
+
`pnpm install` resolves `@sublang/cligent` (^0.4.0) from the registry;
|
|
81
87
|
no local link required. To point pnpm at a local `cligent` checkout
|
|
82
88
|
instead, copy
|
|
83
89
|
[`pnpm-workspace.yaml.example`](pnpm-workspace.yaml.example)
|
|
@@ -85,11 +91,11 @@ into place; the override is gitignored so it never leaks into a
|
|
|
85
91
|
production install.
|
|
86
92
|
|
|
87
93
|
Drive a Boss turn against the source tree (uses the developer
|
|
88
|
-
[`tmux-play.config.yaml`](tmux-play.config.yaml)
|
|
94
|
+
[`tmux-play.config.yaml`](reference/sdlc/code.playbook/tmux-play.config.yaml)
|
|
89
95
|
that imports the compiled adapter via relative path):
|
|
90
96
|
|
|
91
97
|
```sh
|
|
92
|
-
pnpm exec tmux-play --config tmux-play.config.yaml
|
|
98
|
+
pnpm exec tmux-play --config reference/sdlc/code.playbook/tmux-play.config.yaml
|
|
93
99
|
```
|
|
94
100
|
|
|
95
101
|
`pnpm exec` resolves `tmux-play` from the package's local
|
|
@@ -98,16 +104,16 @@ installed globally.
|
|
|
98
104
|
|
|
99
105
|
### Running a Boss turn
|
|
100
106
|
|
|
101
|
-
The Boss pane
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
The Boss pane takes plain-language turns; the judge classifies each
|
|
108
|
+
into an FSM event (start a coding turn, continue or summarize an IR,
|
|
109
|
+
interrupt to a named state, or nothing) per
|
|
110
|
+
[PBRT-1](specs/user/playbook-runtime.md#pbrt-1).
|
|
111
|
+
When a player surfaces a clarifying question
|
|
112
|
+
the FSM parks at `awaitBossReply` and the pane shows the question; your
|
|
113
|
+
next turn is normally classified as the reply, or a fresh directive
|
|
114
|
+
abandons it ([PBRT-2](specs/user/playbook-runtime.md#pbrt-2)).
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
streams the state machine progression with a four-glyph vocabulary —
|
|
116
|
+
The Captain pane streams the state machine with a four-glyph vocabulary —
|
|
111
117
|
`◆ ▸ ⮕ ⤷` per [PBRT-3](specs/user/playbook-runtime.md#pbrt-3) — while
|
|
112
118
|
player prompts ride their own panes.
|
|
113
119
|
|
|
@@ -135,14 +141,14 @@ const runtime = createPlaybookRuntime({
|
|
|
135
141
|
|
|
136
142
|
await runtime.init(ports);
|
|
137
143
|
await runtime.handleBossInput({
|
|
138
|
-
text: '
|
|
144
|
+
text: 'Start fixing the bug',
|
|
139
145
|
signal: new AbortController().signal,
|
|
140
146
|
});
|
|
141
147
|
await runtime.dispose();
|
|
142
148
|
```
|
|
143
149
|
|
|
144
150
|
See
|
|
145
|
-
[`code.playbook.test.ts`](code.playbook.test.ts)
|
|
151
|
+
[`code.playbook.test.ts`](reference/sdlc/code.playbook/code.playbook.test.ts)
|
|
146
152
|
for the full range of port shapes (classifier, judge, abort, interrupt,
|
|
147
153
|
status/telemetry) the runtime is contract-tested against.
|
|
148
154
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sublang/playbook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Reference CODE playbook — XState v5 FSM, host-agnostic runtime, and tmux-play adapter for a coder/reviewer/committer loop driven by GEARS spec items.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,34 +25,34 @@
|
|
|
25
25
|
"node": ">=20.6.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"code.fsm.ts",
|
|
29
|
-
"code.fsm.js",
|
|
30
|
-
"code.fsm.d.ts",
|
|
31
|
-
"code.fsm.introspect.ts",
|
|
32
|
-
"code.fsm.introspect.js",
|
|
33
|
-
"code.fsm.introspect.d.ts",
|
|
34
|
-
"code.playbook.ts",
|
|
35
|
-
"code.playbook.js",
|
|
36
|
-
"code.playbook.d.ts",
|
|
37
|
-
"code.tmux-play.ts",
|
|
38
|
-
"code.tmux-play.js",
|
|
39
|
-
"code.tmux-play.d.ts",
|
|
40
|
-
"code.gears.md",
|
|
41
|
-
"tmux-play.config.yaml",
|
|
42
|
-
"tmux-play.production.config.yaml",
|
|
43
|
-
"bin"
|
|
28
|
+
"reference/sdlc/code.playbook/code.fsm.ts",
|
|
29
|
+
"reference/sdlc/code.playbook/code.fsm.js",
|
|
30
|
+
"reference/sdlc/code.playbook/code.fsm.d.ts",
|
|
31
|
+
"reference/sdlc/code.playbook/code.fsm.introspect.ts",
|
|
32
|
+
"reference/sdlc/code.playbook/code.fsm.introspect.js",
|
|
33
|
+
"reference/sdlc/code.playbook/code.fsm.introspect.d.ts",
|
|
34
|
+
"reference/sdlc/code.playbook/code.playbook.ts",
|
|
35
|
+
"reference/sdlc/code.playbook/code.playbook.js",
|
|
36
|
+
"reference/sdlc/code.playbook/code.playbook.d.ts",
|
|
37
|
+
"reference/sdlc/code.playbook/code.tmux-play.ts",
|
|
38
|
+
"reference/sdlc/code.playbook/code.tmux-play.js",
|
|
39
|
+
"reference/sdlc/code.playbook/code.tmux-play.d.ts",
|
|
40
|
+
"reference/sdlc/code.playbook/code.gears.md",
|
|
41
|
+
"reference/sdlc/code.playbook/tmux-play.config.yaml",
|
|
42
|
+
"reference/sdlc/code.playbook/tmux-play.production.config.yaml",
|
|
43
|
+
"reference/sdlc/code.playbook/bin"
|
|
44
44
|
],
|
|
45
45
|
"bin": {
|
|
46
|
-
"playbook-code": "bin/playbook-code.js"
|
|
46
|
+
"playbook-code": "reference/sdlc/code.playbook/bin/playbook-code.js"
|
|
47
47
|
},
|
|
48
48
|
"exports": {
|
|
49
49
|
"./code/playbook": {
|
|
50
|
-
"types": "./code.playbook.d.ts",
|
|
51
|
-
"default": "./code.playbook.js"
|
|
50
|
+
"types": "./reference/sdlc/code.playbook/code.playbook.d.ts",
|
|
51
|
+
"default": "./reference/sdlc/code.playbook/code.playbook.js"
|
|
52
52
|
},
|
|
53
53
|
"./code/tmux-play": {
|
|
54
|
-
"types": "./code.tmux-play.d.ts",
|
|
55
|
-
"default": "./code.tmux-play.js"
|
|
54
|
+
"types": "./reference/sdlc/code.playbook/code.tmux-play.d.ts",
|
|
55
|
+
"default": "./reference/sdlc/code.playbook/code.tmux-play.js"
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
@@ -4,6 +4,13 @@ type WorkflowKind = 'singleCommit' | 'iteration' | 'specSummary';
|
|
|
4
4
|
type ChangeOrigin = 'bossIntent' | 'irTask';
|
|
5
5
|
type ReviewSubject = 'commit' | 'changes';
|
|
6
6
|
type AfterReview = 'continueIr' | 'summarizeSpecs' | 'done';
|
|
7
|
+
type ResumableStateId = Exclude<JumpableStateId, 'ready' | 'failed'>;
|
|
8
|
+
type PendingBossQuestion = {
|
|
9
|
+
resumeStateId: ResumableStateId;
|
|
10
|
+
sourceItem: string;
|
|
11
|
+
player: Player;
|
|
12
|
+
question: string;
|
|
13
|
+
};
|
|
7
14
|
export type CaptainInput = {
|
|
8
15
|
player: Player;
|
|
9
16
|
sourceItem: string;
|
|
@@ -16,6 +23,8 @@ export type CaptainInput = {
|
|
|
16
23
|
challenges?: string;
|
|
17
24
|
coderPlayer?: string;
|
|
18
25
|
reviewerPlayer?: string;
|
|
26
|
+
pendingBossQuestion?: PendingBossQuestion;
|
|
27
|
+
bossReply?: string;
|
|
19
28
|
};
|
|
20
29
|
export type CaptainOutput = {
|
|
21
30
|
guard: string;
|
|
@@ -24,6 +33,7 @@ export type CaptainOutput = {
|
|
|
24
33
|
reviews?: string;
|
|
25
34
|
challenges?: string;
|
|
26
35
|
summary?: string;
|
|
36
|
+
question?: string;
|
|
27
37
|
[k: string]: unknown;
|
|
28
38
|
};
|
|
29
39
|
export type CodingInput = {
|
|
@@ -43,6 +53,8 @@ export type CodingContext = CodingInput & {
|
|
|
43
53
|
challenges?: string;
|
|
44
54
|
lastResult?: CaptainOutput;
|
|
45
55
|
lastError?: unknown;
|
|
56
|
+
pendingBossQuestion?: PendingBossQuestion;
|
|
57
|
+
bossReply?: string;
|
|
46
58
|
};
|
|
47
59
|
export type CodingEvent = {
|
|
48
60
|
type: 'START_CODING';
|
|
@@ -58,6 +70,9 @@ export type CodingEvent = {
|
|
|
58
70
|
targetId: JumpableStateId;
|
|
59
71
|
intent?: string;
|
|
60
72
|
irNumber?: string;
|
|
73
|
+
} | {
|
|
74
|
+
type: 'BOSS_REPLY';
|
|
75
|
+
answer: string;
|
|
61
76
|
};
|
|
62
77
|
export declare const codingMachine: import("xstate").StateMachine<CodingContext, {
|
|
63
78
|
type: "START_CODING";
|
|
@@ -73,6 +88,9 @@ export declare const codingMachine: import("xstate").StateMachine<CodingContext,
|
|
|
73
88
|
targetId: JumpableStateId;
|
|
74
89
|
intent?: string;
|
|
75
90
|
irNumber?: string;
|
|
91
|
+
} | {
|
|
92
|
+
type: "BOSS_REPLY";
|
|
93
|
+
answer: string;
|
|
76
94
|
}, {
|
|
77
95
|
[x: string]: import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<CaptainOutput, CaptainInput, import("xstate").EventObject>> | undefined;
|
|
78
96
|
}, {
|
|
@@ -9,6 +9,25 @@ export interface CaptainTransition {
|
|
|
9
9
|
readonly index: number;
|
|
10
10
|
readonly target: string;
|
|
11
11
|
readonly guard: TransitionGuard;
|
|
12
|
+
readonly actions: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface AwaitBossReplyInfo {
|
|
15
|
+
readonly stateId: string;
|
|
16
|
+
readonly bossReplyTransitions: ReadonlyArray<BossReplyTransition>;
|
|
17
|
+
readonly transitions: ReadonlyArray<AwaitBossReplyTransition>;
|
|
18
|
+
}
|
|
19
|
+
export interface BossReplyTransition {
|
|
20
|
+
readonly index: number;
|
|
21
|
+
readonly target: string;
|
|
22
|
+
readonly guard: TransitionGuard;
|
|
23
|
+
readonly actions: unknown;
|
|
24
|
+
}
|
|
25
|
+
export interface AwaitBossReplyTransition {
|
|
26
|
+
readonly eventType: string;
|
|
27
|
+
readonly index: number;
|
|
28
|
+
readonly target: string;
|
|
29
|
+
readonly guard: TransitionGuard;
|
|
30
|
+
readonly actions: unknown;
|
|
12
31
|
}
|
|
13
32
|
export type TransitionGuard = (args: {
|
|
14
33
|
context: CodingContext;
|
|
@@ -25,6 +44,11 @@ export interface RootEventTable {
|
|
|
25
44
|
readonly target: string;
|
|
26
45
|
};
|
|
27
46
|
readonly bossInterruptTargets: ReadonlyArray<string>;
|
|
47
|
+
readonly bossInterruptTargetDescriptions: ReadonlyArray<{
|
|
48
|
+
readonly stateId: string;
|
|
49
|
+
readonly description: string;
|
|
50
|
+
}>;
|
|
28
51
|
}
|
|
29
52
|
export declare function enumerateCaptainStates(machine: typeof codingMachine): readonly CaptainStateInfo[];
|
|
30
53
|
export declare function enumerateRootEvents(machine: typeof codingMachine): RootEventTable;
|
|
54
|
+
export declare function enumerateAwaitBossReply(machine: typeof codingMachine): AwaitBossReplyInfo;
|
|
@@ -15,6 +15,7 @@ export function enumerateCaptainStates(machine) {
|
|
|
15
15
|
index,
|
|
16
16
|
target: stripIdPrefix(String(arm.target ?? '')),
|
|
17
17
|
guard: arm.guard ?? alwaysTrue,
|
|
18
|
+
actions: arm.actions,
|
|
18
19
|
}));
|
|
19
20
|
out.push({
|
|
20
21
|
stateId,
|
|
@@ -29,13 +30,39 @@ export function enumerateRootEvents(machine) {
|
|
|
29
30
|
const cfg = getRawConfig(machine);
|
|
30
31
|
const readyOn = (cfg.states?.ready?.on ?? {});
|
|
31
32
|
const rootOn = (cfg.on ?? {});
|
|
33
|
+
const bossInterruptTargets = toArmArray(rootOn.BOSS_INTERRUPT).map((arm) => stripIdPrefix(String(arm.target ?? '')));
|
|
32
34
|
return {
|
|
33
35
|
startCoding: { target: stripIdPrefix(String(readyOn.START_CODING?.target ?? '')) },
|
|
34
36
|
continueIr: { target: stripIdPrefix(String(readyOn.CONTINUE_IR?.target ?? '')) },
|
|
35
37
|
summarizeIr: { target: stripIdPrefix(String(readyOn.SUMMARIZE_IR?.target ?? '')) },
|
|
36
|
-
bossInterruptTargets
|
|
38
|
+
bossInterruptTargets,
|
|
39
|
+
bossInterruptTargetDescriptions: bossInterruptTargets.map((stateId) => {
|
|
40
|
+
const description = cfg.states?.[stateId]?.description;
|
|
41
|
+
return {
|
|
42
|
+
stateId,
|
|
43
|
+
description: typeof description === 'string' ? description : '',
|
|
44
|
+
};
|
|
45
|
+
}),
|
|
37
46
|
};
|
|
38
47
|
}
|
|
48
|
+
export function enumerateAwaitBossReply(machine) {
|
|
49
|
+
const stateId = 'awaitBossReply';
|
|
50
|
+
const awaitOn = getRawConfig(machine).states?.[stateId]?.on ?? {};
|
|
51
|
+
const transitions = Object.entries(awaitOn).flatMap(([eventType, value]) => toArmArray(value).map((arm, index) => ({
|
|
52
|
+
eventType,
|
|
53
|
+
index,
|
|
54
|
+
target: stripIdPrefix(String(arm.target ?? '')),
|
|
55
|
+
guard: arm.guard ?? alwaysTrue,
|
|
56
|
+
actions: arm.actions,
|
|
57
|
+
})));
|
|
58
|
+
const bossReplyTransitions = toArmArray(awaitOn.BOSS_REPLY).map((arm, index) => ({
|
|
59
|
+
index,
|
|
60
|
+
target: stripIdPrefix(String(arm.target ?? '')),
|
|
61
|
+
guard: arm.guard ?? alwaysTrue,
|
|
62
|
+
actions: arm.actions,
|
|
63
|
+
}));
|
|
64
|
+
return { stateId, bossReplyTransitions, transitions };
|
|
65
|
+
}
|
|
39
66
|
function getRawConfig(machine) {
|
|
40
67
|
return machine.config;
|
|
41
68
|
}
|
|
@@ -34,6 +34,28 @@ export interface CaptainTransition {
|
|
|
34
34
|
readonly index: number;
|
|
35
35
|
readonly target: string;
|
|
36
36
|
readonly guard: TransitionGuard;
|
|
37
|
+
readonly actions: unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AwaitBossReplyInfo {
|
|
41
|
+
readonly stateId: string;
|
|
42
|
+
readonly bossReplyTransitions: ReadonlyArray<BossReplyTransition>;
|
|
43
|
+
readonly transitions: ReadonlyArray<AwaitBossReplyTransition>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface BossReplyTransition {
|
|
47
|
+
readonly index: number;
|
|
48
|
+
readonly target: string;
|
|
49
|
+
readonly guard: TransitionGuard;
|
|
50
|
+
readonly actions: unknown;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface AwaitBossReplyTransition {
|
|
54
|
+
readonly eventType: string;
|
|
55
|
+
readonly index: number;
|
|
56
|
+
readonly target: string;
|
|
57
|
+
readonly guard: TransitionGuard;
|
|
58
|
+
readonly actions: unknown;
|
|
37
59
|
}
|
|
38
60
|
|
|
39
61
|
export type TransitionGuard = (args: {
|
|
@@ -46,6 +68,10 @@ export interface RootEventTable {
|
|
|
46
68
|
readonly continueIr: { readonly target: string };
|
|
47
69
|
readonly summarizeIr: { readonly target: string };
|
|
48
70
|
readonly bossInterruptTargets: ReadonlyArray<string>;
|
|
71
|
+
readonly bossInterruptTargetDescriptions: ReadonlyArray<{
|
|
72
|
+
readonly stateId: string;
|
|
73
|
+
readonly description: string;
|
|
74
|
+
}>;
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
type RawInvoke = {
|
|
@@ -54,9 +80,14 @@ type RawInvoke = {
|
|
|
54
80
|
onDone?: unknown;
|
|
55
81
|
};
|
|
56
82
|
|
|
57
|
-
type RawStateDef = {
|
|
83
|
+
type RawStateDef = {
|
|
84
|
+
description?: unknown;
|
|
85
|
+
invoke?: RawInvoke;
|
|
86
|
+
on?: Record<string, unknown>;
|
|
87
|
+
};
|
|
58
88
|
|
|
59
89
|
type RawArm = { target?: unknown; guard?: TransitionGuard };
|
|
90
|
+
type RawArmWithActions = RawArm & { actions?: unknown };
|
|
60
91
|
|
|
61
92
|
type RawConfig = {
|
|
62
93
|
states?: Record<string, RawStateDef>;
|
|
@@ -82,6 +113,7 @@ export function enumerateCaptainStates(
|
|
|
82
113
|
index,
|
|
83
114
|
target: stripIdPrefix(String(arm.target ?? '')),
|
|
84
115
|
guard: arm.guard ?? alwaysTrue,
|
|
116
|
+
actions: arm.actions,
|
|
85
117
|
}),
|
|
86
118
|
);
|
|
87
119
|
out.push({
|
|
@@ -103,23 +135,58 @@ export function enumerateRootEvents(
|
|
|
103
135
|
{ target?: string }
|
|
104
136
|
>;
|
|
105
137
|
const rootOn = (cfg.on ?? {}) as Record<string, unknown>;
|
|
138
|
+
const bossInterruptTargets = toArmArray(rootOn.BOSS_INTERRUPT).map((arm) =>
|
|
139
|
+
stripIdPrefix(String(arm.target ?? '')),
|
|
140
|
+
);
|
|
106
141
|
return {
|
|
107
142
|
startCoding: { target: stripIdPrefix(String(readyOn.START_CODING?.target ?? '')) },
|
|
108
143
|
continueIr: { target: stripIdPrefix(String(readyOn.CONTINUE_IR?.target ?? '')) },
|
|
109
144
|
summarizeIr: { target: stripIdPrefix(String(readyOn.SUMMARIZE_IR?.target ?? '')) },
|
|
110
|
-
bossInterruptTargets
|
|
111
|
-
|
|
112
|
-
|
|
145
|
+
bossInterruptTargets,
|
|
146
|
+
bossInterruptTargetDescriptions: bossInterruptTargets.map((stateId) => {
|
|
147
|
+
const description = cfg.states?.[stateId]?.description;
|
|
148
|
+
return {
|
|
149
|
+
stateId,
|
|
150
|
+
description: typeof description === 'string' ? description : '',
|
|
151
|
+
};
|
|
152
|
+
}),
|
|
113
153
|
};
|
|
114
154
|
}
|
|
115
155
|
|
|
156
|
+
export function enumerateAwaitBossReply(
|
|
157
|
+
machine: typeof codingMachine,
|
|
158
|
+
): AwaitBossReplyInfo {
|
|
159
|
+
const stateId = 'awaitBossReply';
|
|
160
|
+
const awaitOn = getRawConfig(machine).states?.[stateId]?.on ?? {};
|
|
161
|
+
const transitions = Object.entries(awaitOn).flatMap(([eventType, value]) =>
|
|
162
|
+
toArmArray(value).map(
|
|
163
|
+
(arm, index): AwaitBossReplyTransition => ({
|
|
164
|
+
eventType,
|
|
165
|
+
index,
|
|
166
|
+
target: stripIdPrefix(String(arm.target ?? '')),
|
|
167
|
+
guard: arm.guard ?? alwaysTrue,
|
|
168
|
+
actions: arm.actions,
|
|
169
|
+
}),
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
const bossReplyTransitions = toArmArray(awaitOn.BOSS_REPLY).map(
|
|
173
|
+
(arm, index): BossReplyTransition => ({
|
|
174
|
+
index,
|
|
175
|
+
target: stripIdPrefix(String(arm.target ?? '')),
|
|
176
|
+
guard: arm.guard ?? alwaysTrue,
|
|
177
|
+
actions: arm.actions,
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
return { stateId, bossReplyTransitions, transitions };
|
|
181
|
+
}
|
|
182
|
+
|
|
116
183
|
function getRawConfig(machine: typeof codingMachine): RawConfig {
|
|
117
184
|
return (machine as unknown as { config: RawConfig }).config;
|
|
118
185
|
}
|
|
119
186
|
|
|
120
|
-
function toArmArray(value: unknown):
|
|
187
|
+
function toArmArray(value: unknown): RawArmWithActions[] {
|
|
121
188
|
if (value === undefined || value === null) return [];
|
|
122
|
-
return (Array.isArray(value) ? value : [value]) as
|
|
189
|
+
return (Array.isArray(value) ? value : [value]) as RawArmWithActions[];
|
|
123
190
|
}
|
|
124
191
|
|
|
125
192
|
function stripIdPrefix(target: string): string {
|