@harms-haus/pi-workflows 1.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.
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/docs/architecture.md +318 -0
- package/docs/configuration-reference.md +427 -0
- package/docs/contributing.md +132 -0
- package/docs/examples.md +1242 -0
- package/docs/hook-lifecycle.md +380 -0
- package/docs/state-management.md +534 -0
- package/docs/subworkflows.md +428 -0
- package/docs/template-variables.md +383 -0
- package/docs/testing.md +479 -0
- package/package.json +69 -0
- package/skills/workflow-generation/SKILL.md +272 -0
- package/src/TimerManager.ts +67 -0
- package/src/command.ts +199 -0
- package/src/config/index.ts +11 -0
- package/src/config/loading-parse.ts +205 -0
- package/src/config/loading-phases.ts +78 -0
- package/src/config/loading-resolve.ts +82 -0
- package/src/config/loading.ts +202 -0
- package/src/config/templates.ts +25 -0
- package/src/config/validation.ts +258 -0
- package/src/hooks.ts +265 -0
- package/src/index.ts +98 -0
- package/src/prompts.ts +141 -0
- package/src/renderers.ts +46 -0
- package/src/state.ts +426 -0
- package/src/tool.ts +364 -0
- package/src/types.ts +211 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# Template Variables Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Pi-workflows uses a `{varName}` template syntax throughout its configurable strings — phase instructions, role instructions, block reasons, completion messages, and more. Templates are resolved by `resolveTemplate()` in `src/config/templates.ts`.
|
|
6
|
+
|
|
7
|
+
**Resolution rules:**
|
|
8
|
+
|
|
9
|
+
| Rule | Behavior |
|
|
10
|
+
| -------------------- | ------------------------------------------------------------------- |
|
|
11
|
+
| Matching `{varName}` | Replaced with the corresponding value from the variables map |
|
|
12
|
+
| Unknown `{varName}` | **Left as-is** — the literal text `{varName}` remains in the output |
|
|
13
|
+
| No curly braces | Plain text passes through unchanged |
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// Simplified from src/config/templates.ts
|
|
17
|
+
function resolveTemplate(template: string, vars: Record<string, string>): string {
|
|
18
|
+
return template.replace(/\{(\w+)\}/g, (_match, key: string) => {
|
|
19
|
+
return Object.hasOwn(vars, key) ? vars[key] : `{${key}}`;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> **Where to use templates:** Any configurable string in a workflow definition can contain template variables — `initialMessage`, `roleInstruction`, `advanceReminder`, `blockReasonTemplate`, `completionMessage`, `notDoneReminder`, and each phase's `instructions` frontmatter body. See [Configuration Reference](configuration-reference.md) for where each field lives.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Variable Availability by Context
|
|
29
|
+
|
|
30
|
+
Variables are resolved in different contexts with different sets available. The table below summarizes which variables exist in each context; detailed tables follow.
|
|
31
|
+
|
|
32
|
+
| Variable | `initialMessage` | Phase Instructions / Context Prompt | `blockReasonTemplate` | `completionMessage` | Cancel case | `notDoneReminder` |
|
|
33
|
+
| ---------------------- | :--------------: | :---------------------------------: | :-------------------: | :-----------------: | :---------: | :---------------: |
|
|
34
|
+
| `{workflowName}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
35
|
+
| `{workflowKey}` | ✅ | ✅ | — | — | — | ✅ |
|
|
36
|
+
| `{description}` | ✅ | ✅ | — | — | — | — |
|
|
37
|
+
| `{taskDescription}` | — | — | — | ✅ | ✅ | ✅ |
|
|
38
|
+
| `{taskId}` | — | ✅ | — | ✅ | ✅ | ✅ |
|
|
39
|
+
| `{phaseId}` | — | ✅ | — | — | — | — |
|
|
40
|
+
| `{phaseName}` | — | ✅ | ✅ | — | — | ✅ |
|
|
41
|
+
| `{previousPhaseName}` | — | ✅ | — | — | — | — |
|
|
42
|
+
| `{nextPhaseName}` | — | ✅ | — | — | — | — |
|
|
43
|
+
| `{blockedToolsList}` | — | ✅ | — | — | — | — |
|
|
44
|
+
| `{toolName}` | — | ✅ | ✅ | — | — | — |
|
|
45
|
+
| `{breadcrumbPath}` | — | ✅ | — | — | — | — |
|
|
46
|
+
| `{globalStepCount}` | — | ✅ | — | — | — | — |
|
|
47
|
+
| `{phaseEmoji}` | — | — | — | — | — | ✅ |
|
|
48
|
+
| `{phaseInstructions}` | — | — | — | — | — | ✅ |
|
|
49
|
+
| `{phaseCount}` | — | — | — | ✅ | ✅ | — |
|
|
50
|
+
| `{allowedTools}` | — | — | ✅ | — | — | — |
|
|
51
|
+
| `{firstPhaseId}` | ✅ | — | — | — | — | — |
|
|
52
|
+
| `{firstPhaseName}` | ✅ | — | — | — | — | — |
|
|
53
|
+
| `{firstPhaseEmoji}` | ✅ | — | — | — | — | — |
|
|
54
|
+
| `{firstPhaseProfiles}` | ✅ | — | — | — | — | — |
|
|
55
|
+
|
|
56
|
+
> **Note:** There is no separate `cancelledMessage` field on `WorkflowDefinition`. The cancellation path reuses `completionMessage` with `DEFAULT_CANCELLED_MESSAGE` as fallback. See [Cancelled Message](#cancelled-message) below.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## `initialMessage` Variables
|
|
61
|
+
|
|
62
|
+
Resolved once when the workflow starts (in `src/command.ts`). The `initialMessage` field is defined in `workflow.yaml`.
|
|
63
|
+
|
|
64
|
+
| Variable | Source | Example Value |
|
|
65
|
+
| ---------------------- | ------------------------------------------------------------- | --------------------------------- |
|
|
66
|
+
| `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
|
|
67
|
+
| `{workflowKey}` | Definition map key | `rpir` |
|
|
68
|
+
| `{description}` | User's description argument (trimmed) | `Add login page` |
|
|
69
|
+
| `{firstPhaseId}` | First concrete phase's `id` | `refine` |
|
|
70
|
+
| `{firstPhaseName}` | First concrete phase's `name` | `Refine Requirements` |
|
|
71
|
+
| `{firstPhaseEmoji}` | First concrete phase's `emoji` | `🔍` |
|
|
72
|
+
| `{firstPhaseProfiles}` | First phase's `availableProfiles` joined by `, `, or `(none)` | `planner, researcher` |
|
|
73
|
+
|
|
74
|
+
**Example template:**
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
# workflow.yaml
|
|
78
|
+
initialMessage: |
|
|
79
|
+
Starting {workflowName} ({workflowKey}).
|
|
80
|
+
Task: {description}
|
|
81
|
+
First phase: {firstPhaseEmoji} {firstPhaseName} ({firstPhaseId})
|
|
82
|
+
Available profiles: {firstPhaseProfiles}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Resolved output:**
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Starting Refine, Plan, Implement, Review (rpir).
|
|
89
|
+
Task: Add login page
|
|
90
|
+
First phase: 🔍 Refine Requirements (refine)
|
|
91
|
+
Available profiles: planner, researcher
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> **Note:** If the first phase entry is a subworkflow reference, resolution drills into it to find the first concrete phase.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Phase Instructions Variables
|
|
99
|
+
|
|
100
|
+
Available inside every phase's `instructions` body (the markdown content in each `.md` phase file). Also available in `roleInstruction` and `advanceReminder` — see [Context Prompt Variables](#context-prompt-variables) below.
|
|
101
|
+
|
|
102
|
+
Resolved per-turn in `buildContextPrompt()` (`src/prompts.ts`).
|
|
103
|
+
|
|
104
|
+
| Variable | Source | Example Value |
|
|
105
|
+
| --------------------- | ---------------------------------------------- | --------------------------------- |
|
|
106
|
+
| `{workflowName}` | Top-level workflow `name` | `Refine, Plan, Implement, Review` |
|
|
107
|
+
| `{workflowKey}` | `state.workflowKey` | `rpir` |
|
|
108
|
+
| `{description}` | User's original description | `Add login page` |
|
|
109
|
+
| `{taskId}` | Generated task ID | `wf-1747234567890-a3f2k1` |
|
|
110
|
+
| `{phaseId}` | Current phase `id` | `implement` |
|
|
111
|
+
| `{phaseName}` | Current phase `name` | `Implementation` |
|
|
112
|
+
| `{previousPhaseName}` | Previous phase's `name`, or `(start)` if first | `Planning` |
|
|
113
|
+
| `{nextPhaseName}` | Next phase's `name`, or `DONE` if last | `Review` |
|
|
114
|
+
| `{blockedToolsList}` | Blocked tools joined by `, `, or `(none)` | `edit, write` |
|
|
115
|
+
| `{toolName}` | Always `"workflow_step"` | `workflow_step` |
|
|
116
|
+
| `{breadcrumbPath}` | Breadcrumb trail joined by `>` | `RPIR > Implementation` |
|
|
117
|
+
| `{globalStepCount}` | Monotonically increasing step counter | `3` |
|
|
118
|
+
|
|
119
|
+
**Example phase instructions:**
|
|
120
|
+
|
|
121
|
+
```markdown
|
|
122
|
+
---
|
|
123
|
+
id: implement
|
|
124
|
+
name: Implementation
|
|
125
|
+
emoji: ⚙️
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
You are in phase **{phaseName}** of **{workflowName}** (step {globalStepCount}).
|
|
129
|
+
|
|
130
|
+
Task: {description} (ID: {taskId})
|
|
131
|
+
|
|
132
|
+
The previous phase ({previousPhaseName}) produced a plan. Now implement it.
|
|
133
|
+
When done, use `{toolName}` to advance to {nextPhaseName}.
|
|
134
|
+
|
|
135
|
+
Blocked tools: {blockedToolsList}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Resolved output (inside context prompt):**
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
You are in phase **Implementation** of **Refine, Plan, Implement, Review** (step 3).
|
|
142
|
+
|
|
143
|
+
Task: Add login page (ID: wf-1747234567890-a3f2k1)
|
|
144
|
+
|
|
145
|
+
The previous phase (Planning) produced a plan. Now implement it.
|
|
146
|
+
When done, use `workflow_step` to advance to Review.
|
|
147
|
+
|
|
148
|
+
Blocked tools: edit, write
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Context Prompt Variables
|
|
154
|
+
|
|
155
|
+
The **context prompt** is injected as a hidden message before every agent turn via `buildContextPrompt()` in `src/prompts.ts`. It composes several template strings together using the **same variable map** as phase instructions.
|
|
156
|
+
|
|
157
|
+
The following configurable templates all receive the full phase instructions variable set:
|
|
158
|
+
|
|
159
|
+
| Template Field | Purpose |
|
|
160
|
+
| -------------------- | --------------------------------------------------------------- |
|
|
161
|
+
| `roleInstruction` | Prepended to every context injection — defines the agent's role |
|
|
162
|
+
| `advanceReminder` | Appended to every context injection — reminds agent to advance |
|
|
163
|
+
| Phase `instructions` | The main body of what the agent should do this phase |
|
|
164
|
+
|
|
165
|
+
All three are resolved with the identical variable map documented in [Phase Instructions Variables](#phase-instructions-variables) above.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Block Reason Variables
|
|
170
|
+
|
|
171
|
+
Resolved when a tool call is blocked during a phase (in `src/hooks.ts`, `handleToolCall()`). Used by the `blockReasonTemplate` field.
|
|
172
|
+
|
|
173
|
+
| Variable | Source | Example Value |
|
|
174
|
+
| ---------------- | -------------------------------- | ------------------------------------------- |
|
|
175
|
+
| `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
|
|
176
|
+
| `{phaseName}` | Current phase `name` | `Refine Requirements` |
|
|
177
|
+
| `{toolName}` | The tool name that was blocked | `edit` |
|
|
178
|
+
| `{allowedTools}` | Description of what _is_ allowed | `all except: edit, write` or `read, search` |
|
|
179
|
+
|
|
180
|
+
The `{allowedTools}` value depends on the tool restriction mode:
|
|
181
|
+
|
|
182
|
+
- **Blacklist mode:** `"all except: " + blockedTools.join(", ")` → e.g. `all except: edit, write`
|
|
183
|
+
- **Whitelist mode:** `whitelist.join(", ")` → e.g. `read, search`
|
|
184
|
+
|
|
185
|
+
> **Note:** `workflow_step` is always allowed regardless of blacklist or whitelist configuration, but it is **not** included in the `{allowedTools}` value for whitelist mode. It will never appear in the resolved template.
|
|
186
|
+
|
|
187
|
+
**Example:**
|
|
188
|
+
|
|
189
|
+
Template:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
[workflow] "{toolName}" is blocked during {phaseName}.
|
|
193
|
+
Allowed: {allowedTools}. Use workflow_step when done.
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Resolved (blacklist):
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
[workflow] "edit" is blocked during Refine Requirements.
|
|
200
|
+
Allowed: all except: edit, write. Use workflow_step when done.
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Completion & Reminder Variables
|
|
206
|
+
|
|
207
|
+
### Completion Message (`completionMessage`)
|
|
208
|
+
|
|
209
|
+
Resolved when the workflow reaches the DONE state.
|
|
210
|
+
|
|
211
|
+
| Variable | Source | Example Value |
|
|
212
|
+
| ------------------- | -------------------------------------- | --------------------------------- |
|
|
213
|
+
| `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
|
|
214
|
+
| `{taskDescription}` | User's original description | `Add login page` |
|
|
215
|
+
| `{taskId}` | Generated task ID | `wf-1747234567890-a3f2k1` |
|
|
216
|
+
| `{phaseCount}` | Total phases in the top-level workflow | `4` |
|
|
217
|
+
|
|
218
|
+
### Cancelled Message
|
|
219
|
+
|
|
220
|
+
There is **no separate** `cancelledMessage` field on `WorkflowDefinition`. The cancellation path reuses the same `completionMessage` field, but with a different fallback.
|
|
221
|
+
|
|
222
|
+
**Two cancellation paths exist:**
|
|
223
|
+
|
|
224
|
+
1. **`/cancel-workflow` command** (`src/command.ts`) — Sends a **hardcoded message** with no template resolution at all:
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
❌ **Workflow Cancelled**
|
|
228
|
+
|
|
229
|
+
**Task:** {taskDescription}
|
|
230
|
+
**Task ID:** {taskId}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
This command also unloads the workflow state immediately, so the `agent_end` hook sees null state and does nothing further.
|
|
234
|
+
|
|
235
|
+
2. **`agent_end` hook** (`src/hooks.ts`) — When `state.cancelled === true` and `state.completionNotified === false`, resolves `definition.completionMessage` if set, otherwise falls back to `DEFAULT_CANCELLED_MESSAGE` (not `DEFAULT_COMPLETION_MESSAGE`). Uses the same variable set as the completion message: `{workflowName}`, `{taskDescription}`, `{taskId}`, `{phaseCount}`.
|
|
236
|
+
|
|
237
|
+
**Default cancelled message** (`DEFAULT_CANCELLED_MESSAGE` from `src/prompts.ts`):
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
❌ **{workflowName} Cancelled**
|
|
241
|
+
|
|
242
|
+
**Task:** {taskDescription}
|
|
243
|
+
**Task ID:** {taskId}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Not-Done Reminder (`notDoneReminder`)
|
|
247
|
+
|
|
248
|
+
Resolved when the agent tries to stop while the workflow is still active. Forces the agent to continue.
|
|
249
|
+
|
|
250
|
+
| Variable | Source | Example Value |
|
|
251
|
+
| --------------------- | -------------------------------------- | --------------------------------- |
|
|
252
|
+
| `{workflowName}` | `definition.name` | `Refine, Plan, Implement, Review` |
|
|
253
|
+
| `{taskDescription}` | User's original description | `Add login page` |
|
|
254
|
+
| `{taskId}` | Generated task ID | `wf-1747234567890-a3f2k1` |
|
|
255
|
+
| `{workflowKey}` | `state.workflowKey` | `rpir` |
|
|
256
|
+
| `{phaseName}` | Current phase `name` | `Implementation` |
|
|
257
|
+
| `{phaseEmoji}` | Current phase `emoji` | `⚙️` |
|
|
258
|
+
| `{phaseInstructions}` | Current phase's full instructions text | _(the raw instructions string)_ |
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Default Templates
|
|
263
|
+
|
|
264
|
+
All configurable template fields have sensible defaults defined in `src/prompts.ts` and `src/hooks.ts`. If a field is omitted from the workflow definition, the corresponding default is used.
|
|
265
|
+
|
|
266
|
+
### `DEFAULT_ROLE_INSTRUCTION`
|
|
267
|
+
|
|
268
|
+
Used when `roleInstruction` is not set in the workflow definition.
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
You are the ORCHESTRATOR for this workflow. You must NOT use the edit or write tools directly.
|
|
272
|
+
All implementation work must be delegated to subagents via the delegate_to_subagents tool.
|
|
273
|
+
Follow the phase instructions precisely.
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### `DEFAULT_ADVANCE_REMINDER`
|
|
277
|
+
|
|
278
|
+
Used when `advanceReminder` is not set.
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
When you finish this phase, call the workflow_step tool with action='next' to advance to the next phase. If you need to restart the current scope from the beginning, use action='loop'.
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### `DEFAULT_BLOCK_REASON`
|
|
285
|
+
|
|
286
|
+
Used when `blockReasonTemplate` is not set. Defined in `src/hooks.ts`.
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
[workflow] The tool "{toolName}" is blocked during the {phaseName} phase.
|
|
290
|
+
Refer to the current phase instructions for allowed tools and approaches.
|
|
291
|
+
When finished, call workflow_step to advance to the next phase.
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### `DEFAULT_COMPLETION_MESSAGE`
|
|
295
|
+
|
|
296
|
+
Used when `completionMessage` is not set.
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
✅ **{workflowName} Complete**
|
|
300
|
+
|
|
301
|
+
**Task:** {taskDescription}
|
|
302
|
+
**Task ID:** {taskId}
|
|
303
|
+
**Phases completed:** {phaseCount}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### `DEFAULT_NOT_DONE_REMINDER`
|
|
307
|
+
|
|
308
|
+
Used when `notDoneReminder` is not set.
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
⚠️ The {workflowName} is still active. Current phase: {phaseEmoji} {phaseName}.
|
|
312
|
+
|
|
313
|
+
You must NOT stop yet. The workflow requires you to complete the current phase
|
|
314
|
+
and call workflow_step to advance.
|
|
315
|
+
|
|
316
|
+
Current phase instructions:
|
|
317
|
+
{phaseInstructions}
|
|
318
|
+
|
|
319
|
+
Continue working on the current phase and call workflow_step when done.
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### `DEFAULT_CANCELLED_MESSAGE`
|
|
323
|
+
|
|
324
|
+
Fallback for the cancellation path in `handleAgentEnd` when `completionMessage` is not set on the workflow definition. Note: the `/cancel-workflow` command sends its own hardcoded message and does not use this constant.
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
❌ **{workflowName} Cancelled**
|
|
328
|
+
|
|
329
|
+
**Task:** {taskDescription}
|
|
330
|
+
**Task ID:** {taskId}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Resolution Examples
|
|
336
|
+
|
|
337
|
+
### Full lifecycle trace
|
|
338
|
+
|
|
339
|
+
Given a workflow with `name: "Code Review"`, `commandName: "review"`, and two phases (`gather` → `report`):
|
|
340
|
+
|
|
341
|
+
**1. `/workflow review Check auth module`** — `initialMessage` resolved:
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
{workflowName} → Code Review
|
|
345
|
+
{workflowKey} → code-review
|
|
346
|
+
{description} → Check auth module
|
|
347
|
+
{firstPhaseId} → gather
|
|
348
|
+
{firstPhaseName} → Gather Context
|
|
349
|
+
{firstPhaseEmoji} → 📋
|
|
350
|
+
{firstPhaseProfiles} → (none)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**2. Agent turn during `gather` phase** — phase `instructions` resolved:
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
{workflowName} → Code Review
|
|
357
|
+
{description} → Check auth module
|
|
358
|
+
{taskId} → wf-1747234567890-b2c4d6
|
|
359
|
+
{phaseId} → gather
|
|
360
|
+
{phaseName} → Gather Context
|
|
361
|
+
{previousPhaseName} → (start)
|
|
362
|
+
{nextPhaseName} → Report Findings
|
|
363
|
+
{blockedToolsList} → (none)
|
|
364
|
+
{globalStepCount} → 0
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**3. Agent calls `edit` during a phase with `blacklist: [edit]`** — `blockReasonTemplate` resolved:
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
{workflowName} → Code Review
|
|
371
|
+
{phaseName} → Gather Context
|
|
372
|
+
{toolName} → edit
|
|
373
|
+
{allowedTools} → all except: edit
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**4. Workflow completes** — `completionMessage` resolved:
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
{workflowName} → Code Review
|
|
380
|
+
{taskDescription} → Check auth module
|
|
381
|
+
{taskId} → wf-1747234567890-b2c4d6
|
|
382
|
+
{phaseCount} → 2
|
|
383
|
+
```
|