@fluentcommerce/ai-skills 0.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/LICENSE +21 -0
- package/README.md +622 -0
- package/bin/cli.mjs +1973 -0
- package/content/cli/agents/fluent-cli/agent.json +149 -0
- package/content/cli/agents/fluent-cli.md +132 -0
- package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
- package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
- package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
- package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
- package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
- package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
- package/content/cli/skills/fluent-connect/SKILL.md +886 -0
- package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
- package/content/cli/skills/fluent-profile/SKILL.md +180 -0
- package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
- package/content/dev/agents/fluent-dev/agent.json +88 -0
- package/content/dev/agents/fluent-dev.md +525 -0
- package/content/dev/reference-modules/catalog.json +4754 -0
- package/content/dev/skills/fluent-build/SKILL.md +192 -0
- package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
- package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
- package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
- package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
- package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
- package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
- package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
- package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
- package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
- package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
- package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
- package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
- package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
- package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
- package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
- package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
- package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
- package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
- package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
- package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
- package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
- package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
- package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
- package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
- package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
- package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
- package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
- package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
- package/content/mcp-extn/agents/fluent-mcp.md +69 -0
- package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
- package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
- package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
- package/content/rfl/agents/fluent-rfl.md +56 -0
- package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
- package/docs/CAPABILITY_MAP.md +77 -0
- package/docs/CLI_COVERAGE.md +47 -0
- package/docs/DEV_WORKFLOW.md +802 -0
- package/docs/FLOW_RUN.md +142 -0
- package/docs/USE_CASES.md +404 -0
- package/metadata.json +156 -0
- package/package.json +51 -0
|
@@ -0,0 +1,959 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fluent-workflow-analyzer
|
|
3
|
+
description: Analyze Fluent Commerce workflow JSON definitions. Map status graphs, trace event chains, detect orphaned rulesets, validate trigger coverage, and generate dependency reports. Triggers on "analyze workflow", "workflow analysis", "workflow graph", "workflow dependencies", "workflow review".
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
|
|
6
|
+
argument-hint: <workflow-file-or-name> [--deep] [--mermaid] [--explain-rules] [--output <path>]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Workflow Analyzer
|
|
10
|
+
|
|
11
|
+
Analyze existing Fluent Commerce workflow definitions to understand structure, trace event chains, detect issues, and generate comprehensive reports.
|
|
12
|
+
|
|
13
|
+
## Ownership Boundary
|
|
14
|
+
|
|
15
|
+
This skill owns workflow structural analysis, dependency mapping, and quality assessment.
|
|
16
|
+
|
|
17
|
+
Operational workflow commands are owned by `/fluent-workflow`.
|
|
18
|
+
Workflow creation and editing guidance is owned by `/fluent-workflow-builder`.
|
|
19
|
+
Runtime event tracing is owned by `/fluent-trace`.
|
|
20
|
+
|
|
21
|
+
## Pre-Check: Load Source Analysis
|
|
22
|
+
|
|
23
|
+
Before analyzing, check if `/fluent-custom-code` analysis artifacts exist:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
accounts/<PROFILE>/analysis/custom-code/source-map.json
|
|
27
|
+
accounts/<PROFILE>/analysis/custom-code/workflow-rule-map.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If found, use them to:
|
|
31
|
+
- Annotate workflow graph nodes with rule descriptions from `source-map.json`
|
|
32
|
+
- Identify which rules are custom vs core (custom rules appear in `source-map.json`)
|
|
33
|
+
- Report test coverage per rule from `source-map.json` → `modules[].rules[].hasTest`
|
|
34
|
+
|
|
35
|
+
If not found, analyze workflow structure only (no source-level annotations). Treat artifact-gate failures as "not found" (for example missing required files, missing hashes, or blocking `missingSources` in `constraints.json`).
|
|
36
|
+
|
|
37
|
+
## When to Use
|
|
38
|
+
|
|
39
|
+
- Understanding an unfamiliar workflow before modifying it
|
|
40
|
+
- Auditing a workflow for structural issues (orphans, dead-ends, unreachable statuses)
|
|
41
|
+
- Mapping the complete event chain from entity creation to terminal states
|
|
42
|
+
- Generating Mermaid diagrams for workflow walkthroughs and handovers
|
|
43
|
+
- Explaining which rules/rulesets run, what each consumes, and what each emits
|
|
44
|
+
- Generating documentation for a workflow's state machine
|
|
45
|
+
- Pre-deployment validation beyond basic JSON syntax
|
|
46
|
+
- Comparing workflow complexity across entity types
|
|
47
|
+
|
|
48
|
+
## Analysis Framework
|
|
49
|
+
|
|
50
|
+
### Step 1: Load and Parse
|
|
51
|
+
|
|
52
|
+
Load the workflow JSON (from file or download via `/fluent-workflow`):
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
1. Read the workflow JSON file
|
|
56
|
+
2. Extract metadata: name, version, entityType, entitySubtype, retailerId
|
|
57
|
+
3. Separate statuses by entityType (ORDER vs FULFILMENT vs ARTICLE etc.)
|
|
58
|
+
4. Index rulesets by name and type
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Step 2: Build Status Graph
|
|
62
|
+
|
|
63
|
+
Map all status transitions by tracing `SetState` rules:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
For each ruleset:
|
|
67
|
+
1. Find all SetState rules → extract target status
|
|
68
|
+
2. Find all triggers → extract source statuses
|
|
69
|
+
3. If no triggers → ruleset fires on named event (ruleset name = event name)
|
|
70
|
+
4. Map: source_status → [event_name] → target_status
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Output format — Status Transition Table:**
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
Source Status Event/Ruleset Target Status
|
|
77
|
+
-------------------------------------------------------------
|
|
78
|
+
(new entity) CREATE CREATED
|
|
79
|
+
CREATED ProcessOrder ON_VALIDATION
|
|
80
|
+
ON_VALIDATION ConfirmValidation IN_PROGRESS
|
|
81
|
+
IN_PROGRESS OrderComplete COMPLETE
|
|
82
|
+
BOOKED OrderCancel CANCELLED
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Step 3: Trace Event Chains
|
|
86
|
+
|
|
87
|
+
Map cascading events — when one ruleset fires `SendEvent`, trace to the receiving ruleset:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
For each ruleset:
|
|
91
|
+
For each SendEvent rule:
|
|
92
|
+
1. Extract eventName prop
|
|
93
|
+
2. Find matching ruleset (name == eventName)
|
|
94
|
+
3. Record chain: RulesetA -> SendEvent(X) -> RulesetX
|
|
95
|
+
4. Recurse into RulesetX
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Output format — Event Chain:**
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
CREATE
|
|
102
|
+
+-> SendEvent("CheckOrderCoordinates")
|
|
103
|
+
+-> CheckOrderCoordinates
|
|
104
|
+
|-> ForwardIfOrderCoordinatesPresent -> SendEvent("ProcessOrder")
|
|
105
|
+
| +-> ProcessOrder
|
|
106
|
+
| +-> SetState(ON_VALIDATION)
|
|
107
|
+
| +-> SendEvent("FindAndCreateOrderFulfilment")
|
|
108
|
+
+-> ForwardIfOrderCoordinatesNotPresent -> SendEvent("ResolveOrderCoordinates")
|
|
109
|
+
+-> ResolveOrderCoordinates
|
|
110
|
+
+-> SendEvent("UpdateOrderCoordinates")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 3A: Event-to-Ruleset Mapping Heuristics
|
|
114
|
+
|
|
115
|
+
Use these heuristics when mapping runtime events back to workflow intent:
|
|
116
|
+
|
|
117
|
+
1. **Direct name match (highest confidence)**
|
|
118
|
+
`event.name == ruleset.name` on same entity type/subtype.
|
|
119
|
+
2. **Status-trigger fallback**
|
|
120
|
+
If no name match, map by `triggers[].status` against entity status at event time.
|
|
121
|
+
3. **Entity scope check**
|
|
122
|
+
Validate `context.entityType` and `context.rootEntityType` alignment before concluding ownership.
|
|
123
|
+
4. **SendEvent bridge inference**
|
|
124
|
+
For downstream events, map `SendEvent(eventName)` producer ruleset -> target ruleset named `eventName`.
|
|
125
|
+
5. **Audit evidence confirmation**
|
|
126
|
+
Confirm inferred mapping with `ORCHESTRATION_AUDIT` evidence (`category=ruleSet|rule|ACTION|exception`) from `/fluent-trace`.
|
|
127
|
+
|
|
128
|
+
Confidence levels:
|
|
129
|
+
- **Confirmed:** name match + audit evidence
|
|
130
|
+
- **Strong inference:** status-trigger + consistent audit timeline
|
|
131
|
+
- **Weak inference:** no audit evidence; mark explicitly and avoid hard conclusions
|
|
132
|
+
|
|
133
|
+
### Step 3B: Mermaid Diagram Output (--mermaid)
|
|
134
|
+
|
|
135
|
+
When `--mermaid` is requested, generate copy-pasteable Mermaid diagram blocks. Generate **all four diagram types** by default; the user can request a subset. **Validate all blocks against `/fluent-mermaid-validate` rules before output** (no colons in free text, no unicode arrows, quoted special-char labels).
|
|
136
|
+
|
|
137
|
+
#### Diagram 1 — Status State Machine (`stateDiagram-v2`)
|
|
138
|
+
|
|
139
|
+
Generate one state diagram **per entity type** in the workflow (ORDER, FULFILMENT_CHOICE, FULFILMENT, etc.).
|
|
140
|
+
|
|
141
|
+
**Generation algorithm:**
|
|
142
|
+
```
|
|
143
|
+
For each entity type in the workflow:
|
|
144
|
+
1. Collect all statuses for this entity type from statuses[]
|
|
145
|
+
2. Identify DONE-category statuses (terminal states)
|
|
146
|
+
3. For each ruleset scoped to this entity type:
|
|
147
|
+
a. Find trigger status (from triggers[].status) -> this is the source state
|
|
148
|
+
b. Find all SetState rules -> extract target status
|
|
149
|
+
c. Edge = source -> target, labeled with ruleset name
|
|
150
|
+
4. For rulesets with NO trigger status (event-name match):
|
|
151
|
+
a. Infer source status from the event chain (which ruleset sends this event, what status it runs in)
|
|
152
|
+
b. If ambiguous, label edge with "via {eventName}"
|
|
153
|
+
5. [*] -> CREATED for the initial transition
|
|
154
|
+
6. Terminal states -> [*] for DONE-category statuses
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Styling rules:**
|
|
158
|
+
- Use `classDef terminal fill:#d4edda` for completed states, `fill:#f8d7da` for cancelled/failed
|
|
159
|
+
- Add notes for statuses that have ScheduleEvent rules (timeouts)
|
|
160
|
+
- Group subtypes separately if the workflow has subtype-specific rulesets
|
|
161
|
+
|
|
162
|
+
**Example — ORDER entity:**
|
|
163
|
+
```mermaid
|
|
164
|
+
stateDiagram-v2
|
|
165
|
+
[*] --> CREATED: CREATE
|
|
166
|
+
CREATED --> ON_VALIDATION: ProcessOrder
|
|
167
|
+
ON_VALIDATION --> IN_PROGRESS: ConfirmValidation
|
|
168
|
+
ON_VALIDATION --> IN_PROGRESS: ValidationGraceExpired
|
|
169
|
+
IN_PROGRESS --> SHIPPED: NotifyFulfilmentShipped
|
|
170
|
+
SHIPPED --> COMPLETED: NotifyFulfilmentDelivered
|
|
171
|
+
IN_PROGRESS --> COMPLETED: NotifyFulfilmentCollected
|
|
172
|
+
CREATED --> CANCELLED: CancelOrder
|
|
173
|
+
ON_VALIDATION --> CANCELLED: CancelOrder
|
|
174
|
+
COMPLETED --> [*]
|
|
175
|
+
CANCELLED --> [*]
|
|
176
|
+
|
|
177
|
+
note right of ON_VALIDATION: ScheduleEvent: ValidationGraceExpired (30s)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Example — FULFILMENT entity (with subtype branching):**
|
|
181
|
+
```mermaid
|
|
182
|
+
stateDiagram-v2
|
|
183
|
+
[*] --> CREATED: CREATE
|
|
184
|
+
CREATED --> READY: ProcessFulfilment
|
|
185
|
+
READY --> PICKPACK: ConfirmPick
|
|
186
|
+
PICKPACK --> SHIPPED: ConfirmShipment
|
|
187
|
+
PICKPACK --> READY_FOR_PICKUP: ReadyForPickup [CC_PFS only]
|
|
188
|
+
SHIPPED --> DELIVERED: ConfirmDelivery
|
|
189
|
+
READY_FOR_PICKUP --> COLLECTED: ConfirmCollection [CC_PFS only]
|
|
190
|
+
DELIVERED --> [*]
|
|
191
|
+
COLLECTED --> [*]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### Diagram 2 — Event Chain Flowchart (`flowchart TD`)
|
|
195
|
+
|
|
196
|
+
Shows how events cascade through rulesets. Use subgraphs to group by entity type.
|
|
197
|
+
|
|
198
|
+
**Generation algorithm:**
|
|
199
|
+
```
|
|
200
|
+
For each ruleset:
|
|
201
|
+
1. Create a node with shape based on trigger type
|
|
202
|
+
2. For each rule in the ruleset:
|
|
203
|
+
a. SendEvent -> event node (circle) -> edge to target ruleset
|
|
204
|
+
b. SetState -> state node (trapezoid)
|
|
205
|
+
3. Group by entity type using subgraph blocks
|
|
206
|
+
4. Cross-entity bridges use dashed arrows with label
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Node shapes:**
|
|
210
|
+
- `["name"]` — status-triggered ruleset (rectangle)
|
|
211
|
+
- `[/"name"/]` — event-triggered ruleset (parallelogram)
|
|
212
|
+
- `(("eventName"))` — emitted event (circle)
|
|
213
|
+
- `[/STATUS/]` — status change (trapezoid)
|
|
214
|
+
- `{{"condition"}}` — gate/decision rule (hexagon)
|
|
215
|
+
|
|
216
|
+
**Example — Combined flowchart:**
|
|
217
|
+
```mermaid
|
|
218
|
+
flowchart TD
|
|
219
|
+
subgraph ORDER
|
|
220
|
+
CREATE_RS["CREATE<br/><small>trigger: new entity</small>"]
|
|
221
|
+
CREATE_RS --> evt_validate(("ValidateOrder"))
|
|
222
|
+
evt_validate --> VALIDATE_RS[/"ValidateOrder"/]
|
|
223
|
+
VALIDATE_RS --> state_onval[/ON_VALIDATION/]
|
|
224
|
+
|
|
225
|
+
CONFIRM_VAL[/"ConfirmValidation"/]
|
|
226
|
+
CONFIRM_VAL --> evt_process(("ProcessOrder"))
|
|
227
|
+
evt_process --> PROCESS_RS[/"ProcessOrder"/]
|
|
228
|
+
PROCESS_RS --> state_inprog[/IN_PROGRESS/]
|
|
229
|
+
PROCESS_RS --> evt_route(("RouteFulfilmentChoice"))
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
subgraph FULFILMENT_CHOICE
|
|
233
|
+
ROUTE_FC[/"RouteFulfilmentChoice"/]
|
|
234
|
+
evt_route --> ROUTE_FC
|
|
235
|
+
ROUTE_FC --> evt_hd(("ProcessHDFulfilmentChoice"))
|
|
236
|
+
ROUTE_FC --> evt_cc(("ProcessCCFulfilmentChoice"))
|
|
237
|
+
evt_hd --> HD_FC[/"ProcessHDFulfilmentChoice"/]
|
|
238
|
+
evt_cc --> CC_FC[/"ProcessCCFulfilmentChoice"/]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
subgraph FULFILMENT
|
|
242
|
+
HD_FC -.->|CreateFulfilment| FUL_CREATE["CREATE"]
|
|
243
|
+
CC_FC -.->|CreateFulfilment| FUL_CREATE
|
|
244
|
+
FUL_CREATE --> FUL_READY[/READY/]
|
|
245
|
+
CONFIRM_PICK[/"ConfirmPick"/] --> FUL_PICKPACK[/PICKPACK/]
|
|
246
|
+
CONFIRM_SHIP[/"ConfirmShipment"/] --> FUL_SHIPPED[/SHIPPED/]
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
style HD_FC fill:#fff3cd,stroke:#856404
|
|
250
|
+
style CC_FC fill:#fff3cd,stroke:#856404
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Diagram 3 — Cross-Entity Lifecycle (`flowchart LR`)
|
|
254
|
+
|
|
255
|
+
Shows the full lifecycle across ALL entity types on a single horizontal timeline. This is the "big picture" diagram for stakeholder walkthroughs and handovers.
|
|
256
|
+
|
|
257
|
+
**Generation algorithm:**
|
|
258
|
+
```
|
|
259
|
+
1. ORDER statuses as the main spine (left to right)
|
|
260
|
+
2. At each status where child entities are created or events cross entities:
|
|
261
|
+
a. Branch down to FULFILMENT_CHOICE statuses
|
|
262
|
+
b. Branch further down to FULFILMENT statuses
|
|
263
|
+
3. Cross-entity events shown as dashed arrows
|
|
264
|
+
4. User actions (events with userActions config) shown with bold borders
|
|
265
|
+
5. Webhooks shown as external callout nodes
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Example:**
|
|
269
|
+
```mermaid
|
|
270
|
+
flowchart LR
|
|
271
|
+
subgraph Order
|
|
272
|
+
O_CREATED([CREATED]) -->|ProcessOrder| O_ONVAL([ON_VALIDATION])
|
|
273
|
+
O_ONVAL -->|ConfirmValidation| O_INPROG([IN_PROGRESS])
|
|
274
|
+
O_INPROG -->|all fulfilments done| O_SHIPPED([SHIPPED])
|
|
275
|
+
O_SHIPPED -->|all delivered| O_COMPLETE([COMPLETED])
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
subgraph FulfilmentChoice
|
|
279
|
+
O_INPROG -.->|RouteFulfilmentChoice| FC_CREATED([FC:CREATED])
|
|
280
|
+
FC_CREATED -->|ProcessHD/CC| FC_ASSIGNED([FC:ASSIGNED])
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
subgraph Fulfilment
|
|
284
|
+
FC_ASSIGNED -.->|CreateFulfilment| FUL_CREATE([FUL:CREATED])
|
|
285
|
+
FUL_CREATE --> FUL_READY([READY])
|
|
286
|
+
FUL_READY -->|ConfirmPick| FUL_PICK([PICKPACK])
|
|
287
|
+
FUL_PICK -->|Ship| FUL_SHIPPED([SHIPPED])
|
|
288
|
+
FUL_SHIPPED -->|Deliver| FUL_DELIVERED([DELIVERED])
|
|
289
|
+
FUL_PICK -->|CC path| FUL_RFP([READY_FOR_PICKUP])
|
|
290
|
+
FUL_RFP -->|Collect| FUL_COLLECTED([COLLECTED])
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
FUL_SHIPPED -.->|NotifyShipped| O_SHIPPED
|
|
294
|
+
FUL_DELIVERED -.->|NotifyDelivered| O_COMPLETE
|
|
295
|
+
|
|
296
|
+
style O_COMPLETE fill:#d4edda
|
|
297
|
+
style FUL_DELIVERED fill:#d4edda
|
|
298
|
+
style FUL_COLLECTED fill:#d4edda
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### Diagram 4 — Rule Dependency Graph (`flowchart TD`)
|
|
302
|
+
|
|
303
|
+
Shows what each rule in a ruleset consumes (settings, event attributes, entity fields) and produces (state changes, events, mutations). Use `--deep` to generate for all rulesets, otherwise generate for a specific ruleset on request.
|
|
304
|
+
|
|
305
|
+
**Generation algorithm:**
|
|
306
|
+
```
|
|
307
|
+
For a specific ruleset:
|
|
308
|
+
1. For each rule, look up metadata via plugin.list (see Step 3D)
|
|
309
|
+
2. From rule props, identify: settings referenced, event attributes consumed
|
|
310
|
+
3. From rule metadata, identify: events produced, mutations performed
|
|
311
|
+
4. Create nodes: settings (cylinder), events (circle), states (trapezoid), rules (rectangle)
|
|
312
|
+
5. Draw edges: setting --> rule --> event/state
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Example — single ruleset:**
|
|
316
|
+
```mermaid
|
|
317
|
+
flowchart TD
|
|
318
|
+
setting_wh[("webhook.order.created")] --> rule_webhook["SendWebhookWithDynamicAttributes"]
|
|
319
|
+
rule_webhook --> ext_call>"External webhook POST"]
|
|
320
|
+
rule_upsert["UpsertAttribute"] --> attr["statusHistory attribute"]
|
|
321
|
+
rule_setState["SetState"] --> state[/ON_VALIDATION/]
|
|
322
|
+
rule_schedule["ScheduleEvent"] --> timer["ValidationGraceExpired (30s)"]
|
|
323
|
+
rule_sendEvt["SendEvent"] --> evt(("ValidateOrder"))
|
|
324
|
+
|
|
325
|
+
style setting_wh fill:#e2e3f1
|
|
326
|
+
style ext_call fill:#fff3cd
|
|
327
|
+
style timer fill:#fce4ec
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Step 3C: Rule Execution Narrative (--explain-rules)
|
|
331
|
+
|
|
332
|
+
When `--explain-rules` is requested, produce a detailed execution narrative per ruleset. This answers "what happens when this event fires?" in plain language with verified data.
|
|
333
|
+
|
|
334
|
+
#### Rule Lookup Strategy (MANDATORY — never guess what a rule does)
|
|
335
|
+
|
|
336
|
+
To explain a rule, you MUST look it up. Use these sources in priority order:
|
|
337
|
+
|
|
338
|
+
**1. Live rule registry — `plugin.list` (covers ALL deployed rules):**
|
|
339
|
+
```
|
|
340
|
+
plugin.list({ name: "RuleShortName" })
|
|
341
|
+
```
|
|
342
|
+
Returns: description, parameters (with types/defaults), accepted entity types, produced events, exceptions. Extract short name from `ACCOUNT.bundle.RuleName` -> search for `RuleName`.
|
|
343
|
+
|
|
344
|
+
**2. Custom rule source code (if cloned under SOURCE/):**
|
|
345
|
+
```
|
|
346
|
+
accounts/<PROFILE>/SOURCE/<repo>/src/main/java/.../<RuleName>.java
|
|
347
|
+
```
|
|
348
|
+
Look for `@RuleInfo`, `@ParamString`/`@ParamInteger` annotations, `run()` method logic, GraphQL queries/mutations, `sendEvent()` calls.
|
|
349
|
+
|
|
350
|
+
**3. Reference module JARs (for core/order/fulfilment module rules):**
|
|
351
|
+
If `plugin.list` returns a description but you need implementation details for a reference module rule (core, order, fulfilment, commonv2, globalinventory), the user can provide the deployed JAR. Decompile it to inspect the rule's actual logic:
|
|
352
|
+
```
|
|
353
|
+
# Get JAR from Fluent module artifacts or ask user to supply it
|
|
354
|
+
# Decompile with cfr, procyon, or IntelliJ's built-in decompiler
|
|
355
|
+
java -jar cfr.jar <module>.jar --outputdir decompiled/
|
|
356
|
+
# Then search for the rule class — use the Glob tool with pattern
|
|
357
|
+
# decompiled/**/*RuleName* instead of shell find for cross-platform compatibility
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
This is useful when `plugin.list` gives you "what" but you need "how" — the actual algorithm, edge cases, and failure modes. Store decompiled output under `accounts/<PROFILE>/SOURCE/.decompiled/<jar-basename>/`.
|
|
361
|
+
|
|
362
|
+
**4. Cached analysis artifacts (from `/fluent-custom-code`):**
|
|
363
|
+
```
|
|
364
|
+
accounts/<PROFILE>/analysis/custom-code/source-map.json -> modules[].rules[]
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**5. Name pattern inference (LAST RESORT — always label as inferred):**
|
|
368
|
+
Only if all above fail. Infer from name: `*SetState` -> state change, `*SendEvent` -> emit event, etc. Always mark as `(inferred from name — not verified)`.
|
|
369
|
+
|
|
370
|
+
#### Narrative Format
|
|
371
|
+
|
|
372
|
+
For each ruleset, produce:
|
|
373
|
+
|
|
374
|
+
**1. Header:**
|
|
375
|
+
```
|
|
376
|
+
### [RulesetName] — {entityType} @ {triggerStatus or "event: {eventName}"}
|
|
377
|
+
{One-sentence business summary of what this ruleset accomplishes}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**2. Rules table (descriptions from plugin.list, NOT invented):**
|
|
381
|
+
|
|
382
|
+
| # | Rule | Description | Props | Reads | Produces |
|
|
383
|
+
|---|------|-------------|-------|-------|----------|
|
|
384
|
+
| 1 | `ACCT.bundle.RuleName` | *{from plugin.list}* | `prop=value` | event.attr.X | SetState(STATUS) |
|
|
385
|
+
| 2 | `ACCT.core.SendEvent` | *Sends named event* | `eventName=Foo` | — | Event: Foo |
|
|
386
|
+
|
|
387
|
+
**3. Data flow summary:**
|
|
388
|
+
```
|
|
389
|
+
Inputs: event.attributes.items, setting:fulfilment.pick.config
|
|
390
|
+
Actions: Update item quantities, transition to PICKPACK
|
|
391
|
+
Outputs: SendEvent(RequestShipment), SendEvent(ReadyForPickup)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**4. Failure modes (from rule metadata + props):**
|
|
395
|
+
```
|
|
396
|
+
- Missing setting "fulfilment.pick.config" -> SettingNotFoundException
|
|
397
|
+
- Event attribute "items" empty -> rule skips (no error)
|
|
398
|
+
- Entity not in expected status -> ruleset no-match (silent skip)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
#### Full Walkthrough Example
|
|
402
|
+
|
|
403
|
+
```markdown
|
|
404
|
+
### CREATE — ORDER @ new entity
|
|
405
|
+
Initializes a new order: ensures product variants exist, records status history,
|
|
406
|
+
fires webhook to external system, and routes to validation.
|
|
407
|
+
|
|
408
|
+
| # | Rule | Description | Props | Produces |
|
|
409
|
+
|---|------|-------------|-------|----------|
|
|
410
|
+
| 1 | `*.CreateMissingVariantProducts` | Creates variants that don't exist for order items | `productCatalogueRef=PC:MASTER:2` | Product mutations |
|
|
411
|
+
| 2 | `*.UpdateStatusHistory` | Records status transition in entity attributes | — | Attribute upsert |
|
|
412
|
+
| 3 | `*.SendWebhookWithDynamicAttributes` | POSTs to URL from settings | `settingName=webhook.order.created` | HTTP POST |
|
|
413
|
+
| 4 | `*.SendEvent` | Sends named event | `eventName=ValidateOrder` | Event: ValidateOrder |
|
|
414
|
+
|
|
415
|
+
Data flow: New order -> product variants ensured -> status logged -> webhook fired -> ValidateOrder emitted.
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Step 3D: Rule Resolution (Understanding What a Rule Does)
|
|
419
|
+
|
|
420
|
+
When you encounter a rule in a workflow and need to understand its behavior, follow this resolution chain in order:
|
|
421
|
+
|
|
422
|
+
#### 1. Parse the Rule Name
|
|
423
|
+
|
|
424
|
+
Rule names follow `<ACCOUNT>.<MODULE>.<RuleName>`. The module segment tells you the origin:
|
|
425
|
+
|
|
426
|
+
| Module | Origin | Example |
|
|
427
|
+
|--------|--------|---------|
|
|
428
|
+
| `core` | Fluent platform core module | `ACCT.core.SetState`, `ACCT.core.SendEvent` |
|
|
429
|
+
| `order`, `fulfilment` | Fluent reference modules | `ACCT.order.SendEventForAllFulfilments` |
|
|
430
|
+
| `commonv2`, `globalinventory` | Fluent shared/inventory modules | `ACCT.globalinventory.ValidateIncomingProduct` |
|
|
431
|
+
| `reporting` | Fluent reporting module | `ACCT.reporting.SetState` |
|
|
432
|
+
| Any other name | **Custom extension module** | `ACCT.myextensions.CreateFulfilmentFromSourcingLocation` |
|
|
433
|
+
|
|
434
|
+
#### 2. Query the Live Rule Registry (`plugin.list`)
|
|
435
|
+
|
|
436
|
+
The `plugin.list` MCP tool returns the live registry of every rule deployed on the platform. This is the fastest way to understand any rule:
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
plugin.list({ name: "CreateFulfilmentFromSourcingLocation" })
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Returns:**
|
|
443
|
+
- `description` — what the rule does in plain language
|
|
444
|
+
- `parameters[]` — configurable props (`name`, `description`, `type`) that map to the `props` object in workflow JSON
|
|
445
|
+
- `ruleInfo.accepts[]` — entity types the rule works with
|
|
446
|
+
- `ruleInfo.produces[]` — events the rule can emit
|
|
447
|
+
- `ruleInfo.ruleException[]` — exceptions it can throw
|
|
448
|
+
|
|
449
|
+
**Example response (live from SAGIRISH):**
|
|
450
|
+
```json
|
|
451
|
+
{
|
|
452
|
+
"name": "CreateFulfilmentFromSourcingLocation",
|
|
453
|
+
"description": "Creates a Fulfilment at a location dynamically sourced from a configurable JSON path.",
|
|
454
|
+
"parameters": [
|
|
455
|
+
{ "name": "sourcingLocationPath", "description": "JSON path to extract location ref", "type": "STRING" },
|
|
456
|
+
{ "name": "fulfilmentType", "description": "Fulfilment type (default: HD)", "type": "STRING" },
|
|
457
|
+
{ "name": "fulfilmentRefPrefix", "description": "Static fallback prefix (default: FUL)", "type": "STRING" }
|
|
458
|
+
]
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
This tells you: the `props` in the workflow JSON (`sourcingLocationPath`, `fulfilmentType`, etc.) map directly to these parameters.
|
|
463
|
+
|
|
464
|
+
#### 3. Read Source Code or Decompiled JAR
|
|
465
|
+
|
|
466
|
+
For **any** rule where you need implementation details (not just the description from `plugin.list`), look at the actual code:
|
|
467
|
+
|
|
468
|
+
**Custom extension modules** — original source:
|
|
469
|
+
```
|
|
470
|
+
accounts/<PROFILE>/SOURCE/<repo>/src/main/java/.../CreateFulfilmentFromSourcingLocation.java
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Reference modules** (core, order, fulfilment, commonv2, globalinventory) — decompiled from JAR:
|
|
474
|
+
```
|
|
475
|
+
accounts/<PROFILE>/SOURCE/.decompiled/<jar-basename>/com/fluentcommerce/rule/.../SetState.java
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Reference modules ship as JARs. If the user has placed the JAR under `accounts/<PROFILE>/SOURCE/`, `/fluent-connect` or `/fluent-custom-code` will decompile it. If no decompiled source exists yet, ask the user to provide the module JAR:
|
|
479
|
+
|
|
480
|
+
> I need to see the implementation of `<ACCOUNT>.order.SendEventForAllFulfilments`.
|
|
481
|
+
> This is a reference module rule — the source isn't open, but if you have the JAR
|
|
482
|
+
> (e.g., `fc-module-order-<version>.jar`), place it under `accounts/<PROFILE>/SOURCE/`
|
|
483
|
+
> and I can decompile it.
|
|
484
|
+
|
|
485
|
+
**What to look for in source or decompiled code:**
|
|
486
|
+
- `@RuleInfo` annotation — description, accepts, produces (mirrors `plugin.list`)
|
|
487
|
+
- `@ParamString` / `@ParamInteger` annotations — rule parameters
|
|
488
|
+
- `run()` method — the actual execution logic
|
|
489
|
+
- GraphQL queries/mutations — what entity data the rule reads or writes
|
|
490
|
+
- `context.action().sendEvent()` calls — events it emits at runtime
|
|
491
|
+
- Exception patterns — what causes failures
|
|
492
|
+
|
|
493
|
+
**Confidence by source type:**
|
|
494
|
+
|
|
495
|
+
| Source | Confidence | Editable? |
|
|
496
|
+
|--------|-----------|-----------|
|
|
497
|
+
| Original repo source | High | Yes |
|
|
498
|
+
| Decompiled JAR | Medium (synthetic var names, missing comments) | No (read-only) |
|
|
499
|
+
| `plugin.list` only | Description-level (no implementation detail) | N/A |
|
|
500
|
+
|
|
501
|
+
Use `/fluent-custom-code` to generate a pre-computed analysis (`source-map.json`) that indexes all rules — both custom and decompiled — with descriptions, parameters, test coverage, and complexity metrics.
|
|
502
|
+
|
|
503
|
+
#### 4. Check Cached Analysis Artifacts
|
|
504
|
+
|
|
505
|
+
If `/fluent-custom-code` has been run:
|
|
506
|
+
|
|
507
|
+
- `source-map.json` -> `modules[].rules[]` — every rule with `className`, `description`, `parameters`, `hasTest`
|
|
508
|
+
- `workflow-rule-map.json` — which rules are used in which workflows and rulesets
|
|
509
|
+
- `constraints.json` — extension risks and recommendations
|
|
510
|
+
|
|
511
|
+
#### Resolution Priority
|
|
512
|
+
|
|
513
|
+
```
|
|
514
|
+
plugin.list (live, always available, covers ALL deployed rules)
|
|
515
|
+
| need implementation details?
|
|
516
|
+
Original source (custom rules with repos cloned to SOURCE/)
|
|
517
|
+
| no source repo?
|
|
518
|
+
Decompiled JAR (reference or custom modules, under SOURCE/.decompiled/)
|
|
519
|
+
| no JAR available?
|
|
520
|
+
Ask user to provide JAR -> place under SOURCE/ -> decompile
|
|
521
|
+
| need pre-computed batch analysis?
|
|
522
|
+
source-map.json (from /fluent-custom-code, covers both source + decompiled)
|
|
523
|
+
| rule not found anywhere?
|
|
524
|
+
Likely undeployed or from a module not installed on this account
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Step 4: Detect Issues
|
|
528
|
+
|
|
529
|
+
Run these checks against the parsed workflow:
|
|
530
|
+
|
|
531
|
+
#### 4.1 Orphaned Rulesets
|
|
532
|
+
Rulesets that can never be reached:
|
|
533
|
+
- No status trigger AND
|
|
534
|
+
- No other ruleset sends an event matching this ruleset's name
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
For each ruleset R:
|
|
538
|
+
if R has no status triggers:
|
|
539
|
+
if no other ruleset contains SendEvent(R.name):
|
|
540
|
+
WARN: Orphaned ruleset "{R.name}" — never triggered
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
#### 4.2 Dead-End Statuses
|
|
544
|
+
Statuses with no outgoing transitions (no ruleset triggers from them):
|
|
545
|
+
|
|
546
|
+
```
|
|
547
|
+
For each status S:
|
|
548
|
+
if no ruleset has trigger.status == S.name:
|
|
549
|
+
if S.category != "DONE":
|
|
550
|
+
WARN: Dead-end status "{S.name}" (category={S.category}) — no transitions out
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Note: DONE-category statuses are expected terminal states and should NOT be flagged.
|
|
554
|
+
|
|
555
|
+
#### 4.3 Unreachable Statuses
|
|
556
|
+
Statuses that no `SetState` rule targets:
|
|
557
|
+
|
|
558
|
+
```
|
|
559
|
+
For each status S:
|
|
560
|
+
if no ruleset contains SetState(S.name):
|
|
561
|
+
if S.name != "CREATED": # CREATED is set by platform on entity creation
|
|
562
|
+
WARN: Unreachable status "{S.name}" — no SetState targets it
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
#### 4.4 Missing SendEvent Targets
|
|
566
|
+
Events fired by `SendEvent` that have no matching ruleset:
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
For each SendEvent(eventName):
|
|
570
|
+
if no ruleset.name == eventName:
|
|
571
|
+
WARN: SendEvent("{eventName}") has no matching ruleset
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Note: Some events are intentionally sent to child/parent entities or external systems. Cross-reference with entity types.
|
|
575
|
+
|
|
576
|
+
#### 4.5 Same-Name Ruleset Collision Analysis
|
|
577
|
+
```
|
|
578
|
+
For each pair of rulesets (R1, R2) where R1.name == R2.name and R1.type == R2.type:
|
|
579
|
+
key = (type, subtype-or-*, name)
|
|
580
|
+
|
|
581
|
+
if key matches and trigger statuses overlap:
|
|
582
|
+
HIGH: Ambiguous same-name rulesets with overlapping trigger scopes
|
|
583
|
+
else if key matches and trigger statuses do not overlap:
|
|
584
|
+
MEDIUM: Same-name multi-trigger pattern (often intentional, review for clarity)
|
|
585
|
+
else if subtype differs:
|
|
586
|
+
INFO: Same-name subtype-partitioned rulesets (usually intentional)
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Notes:
|
|
590
|
+
- Same name with different entity types (ORDER vs FULFILMENT) is normal.
|
|
591
|
+
- Same name + same type is not automatically an error; subtype and trigger overlap decide severity.
|
|
592
|
+
|
|
593
|
+
#### 4.6 Trigger Conflicts
|
|
594
|
+
Multiple rulesets with the same status trigger for the same entity type:
|
|
595
|
+
|
|
596
|
+
```
|
|
597
|
+
For each status S:
|
|
598
|
+
rulesets = all rulesets with trigger.status == S AND same type
|
|
599
|
+
if len(rulesets) > 1:
|
|
600
|
+
WARN: Multiple rulesets trigger on status "{S}": {ruleset names}
|
|
601
|
+
(Only the first match fires — order matters)
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
#### 4.7 Rule Pattern Detection
|
|
605
|
+
|
|
606
|
+
Detect rule types, integration touchpoints, and setting dependencies by static analysis of rule names and props:
|
|
607
|
+
|
|
608
|
+
**Direct event-name match:**
|
|
609
|
+
- Ruleset name = expected event name (primary matching mechanism)
|
|
610
|
+
- Example: Ruleset `ConfirmValidation` fires when event `ConfirmValidation` arrives
|
|
611
|
+
|
|
612
|
+
**Status-trigger match:**
|
|
613
|
+
- Rulesets with `triggers: [{ "status": "BOOKED" }]` fire when the entity enters that status
|
|
614
|
+
- Check for conflicts: multiple rulesets triggering on the same status (only first match fires)
|
|
615
|
+
|
|
616
|
+
**Cross-entity event mapping:**
|
|
617
|
+
- `SendEvent` rules in one entity type often target rulesets in a different entity type
|
|
618
|
+
- Example: ORDER ruleset sends `CreateFulfilment` -> FULFILMENT entity's `CREATE` ruleset fires
|
|
619
|
+
- Track: source entity type + event name -> target entity type + matching ruleset
|
|
620
|
+
|
|
621
|
+
**Rule pattern detection for status transitions:**
|
|
622
|
+
```
|
|
623
|
+
Status transition rules (use endsWith for namespaced matching):
|
|
624
|
+
- *SetState -> sets entity status
|
|
625
|
+
- *ChangeState -> state change rule
|
|
626
|
+
- *ChangeStateGQL -> GraphQL-based state change
|
|
627
|
+
Example: "ACCOUNT.core.SetState" matches via endsWith("SetState")
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
**Webhook pattern detection:**
|
|
631
|
+
```
|
|
632
|
+
Rules whose names contain (case-insensitive):
|
|
633
|
+
webhook, hook, callback, notify, notification, trigger, publish
|
|
634
|
+
Or whose props reference webhook setting names.
|
|
635
|
+
Mark these rulesets as integration touchpoints.
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
**Setting dependency detection:**
|
|
639
|
+
```
|
|
640
|
+
Rules whose props contain keys matching:
|
|
641
|
+
setting, config, param, parameter, option, preference
|
|
642
|
+
Extract the setting name from the prop value.
|
|
643
|
+
Cross-reference with /fluent-settings to verify settings exist.
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
#### 4.8 Settings Validation (Live Cross-Reference)
|
|
647
|
+
|
|
648
|
+
Verify that all settings referenced by workflow rule props actually exist in the live environment. Missing settings are the #1 cause of silent rule failures — the event processes as SUCCESS but the rule does nothing, leaving entities stuck.
|
|
649
|
+
|
|
650
|
+
##### Extraction
|
|
651
|
+
|
|
652
|
+
Parse all rulesets and extract setting references from rule props:
|
|
653
|
+
|
|
654
|
+
```
|
|
655
|
+
For each ruleset:
|
|
656
|
+
For each rule in ruleset.rules:
|
|
657
|
+
For each prop key/value in rule.props:
|
|
658
|
+
If key matches: setting, settingName, settingRef, settingsKey,
|
|
659
|
+
webhookSettingName, configSetting, configName
|
|
660
|
+
→ Record: { settingName: value, ruleset, rule, propKey }
|
|
661
|
+
|
|
662
|
+
If rule.name endsWith "SendWebhook" or "SendWebhookWithDynamicAttributes":
|
|
663
|
+
→ Mark as webhook setting (expect LOB valueType with "url" field)
|
|
664
|
+
|
|
665
|
+
If rule.name endsWith "UpdateEntityFromSetting" or "GetSettingValue":
|
|
666
|
+
→ Mark as config setting
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Additionally, query `plugin.list` for each rule to discover settings that aren't obvious from prop names:
|
|
670
|
+
|
|
671
|
+
```
|
|
672
|
+
plugin.list({ name: "<RuleShortName>" })
|
|
673
|
+
→ Check parameters[].description for mentions of "setting" or "config"
|
|
674
|
+
→ Cross-reference parameter names with the rule's props to identify the actual setting name
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
##### Validation
|
|
678
|
+
|
|
679
|
+
For each extracted setting reference, query the live environment:
|
|
680
|
+
|
|
681
|
+
```graphql
|
|
682
|
+
{
|
|
683
|
+
settings(first: 1, name: ["<SETTING_NAME>"], context: "RETAILER") {
|
|
684
|
+
edges {
|
|
685
|
+
node { id name value lobValue valueType context contextId }
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
Check:
|
|
692
|
+
1. **Exists** — edges array is non-empty
|
|
693
|
+
2. **Value type** — webhook settings should be LOB; simple config should be STRING/BOOLEAN
|
|
694
|
+
3. **Value content** — LOB webhook settings should contain a `url` field in `lobValue`
|
|
695
|
+
4. **Context scope** — setting context matches rule expectation (usually RETAILER)
|
|
696
|
+
5. **Non-empty** — `value` or `lobValue` is not null/empty
|
|
697
|
+
|
|
698
|
+
##### Report
|
|
699
|
+
|
|
700
|
+
```
|
|
701
|
+
=== Settings Validation: ORDER::HD (v1.50) ===
|
|
702
|
+
|
|
703
|
+
Ruleset Rule Setting Ref Status
|
|
704
|
+
──────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
705
|
+
OrderCreatedWebhook *.SendWebhookWithDynamicAttributes webhook.order.created OK (LOB)
|
|
706
|
+
OrderCancelledWebhook *.SendWebhookWithDynamicAttributes webhook.order.cancelled OK (LOB)
|
|
707
|
+
FulfilmentShippedNotify *.SendWebhookWithDynamicAttributes webhook.fulfilment.shipped MISSING
|
|
708
|
+
UpdateOrderConfig *.UpdateEntityFromSetting order.update.mapping MISSING
|
|
709
|
+
ConsignmentCreate *.GetSettingValue consignment.prefix OK (STRING)
|
|
710
|
+
CarrierRouting *.GetSettingValue carrier.hd.config TYPE_MISMATCH
|
|
711
|
+
→ Expected: LOB (JSON config), Actual: STRING
|
|
712
|
+
|
|
713
|
+
Summary: 3/6 valid, 2 MISSING, 1 TYPE_MISMATCH
|
|
714
|
+
Action: Create missing settings before go-live → /fluent-settings
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
##### Severity
|
|
718
|
+
|
|
719
|
+
| Status | Severity | Impact |
|
|
720
|
+
|--------|----------|--------|
|
|
721
|
+
| OK | None | Setting will be read correctly |
|
|
722
|
+
| MISSING | HIGH | Rule will fail silently — entity gets stuck |
|
|
723
|
+
| TYPE_MISMATCH | MEDIUM | Rule may read empty value (e.g., reads `lobValue` but type is STRING) |
|
|
724
|
+
| EMPTY_VALUE | LOW | Setting exists but value is null/empty — rule behavior depends on implementation |
|
|
725
|
+
|
|
726
|
+
##### Orphaned Settings Detection
|
|
727
|
+
|
|
728
|
+
Optionally, compare all live settings against all setting references extracted from workflows:
|
|
729
|
+
|
|
730
|
+
```
|
|
731
|
+
For each setting in the environment:
|
|
732
|
+
If setting.name does NOT appear in any workflow rule prop:
|
|
733
|
+
→ ORPHANED (exists but no rule uses it — candidate for cleanup)
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
Report orphaned settings separately. These are low priority but indicate configuration drift.
|
|
737
|
+
|
|
738
|
+
#### 4.9 Rule Props Audit (Runtime vs Static)
|
|
739
|
+
|
|
740
|
+
Compare rule props as defined in the workflow JSON against the actual props observed at runtime via ORCHESTRATION_AUDIT events. Drift between static config and runtime evidence indicates workflow version mismatches, environment-specific overrides, or deployment issues.
|
|
741
|
+
|
|
742
|
+
##### Static Props Extraction
|
|
743
|
+
|
|
744
|
+
From the workflow JSON, extract all rule props for each ruleset:
|
|
745
|
+
|
|
746
|
+
```
|
|
747
|
+
For each ruleset:
|
|
748
|
+
For each rule in ruleset.rules:
|
|
749
|
+
Record: { rulesetName, ruleName, props: { key: value, ... } }
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
##### Runtime Props Extraction
|
|
753
|
+
|
|
754
|
+
Query ORCHESTRATION_AUDIT events with `category=rule` for the target entity:
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
event.list({
|
|
758
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
759
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
760
|
+
"count": 200
|
|
761
|
+
})
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
Filter results where `category == "rule"`. Each rule audit event contains:
|
|
765
|
+
- `name` — the rule's short name (e.g., `SendWebhookWithDynamicAttributes`)
|
|
766
|
+
- `ruleSet` — the ruleset it belongs to (e.g., `OrderCreatedWebhook`)
|
|
767
|
+
- `props` — the actual props object as passed by the workflow engine at execution time
|
|
768
|
+
|
|
769
|
+
##### Comparison
|
|
770
|
+
|
|
771
|
+
```
|
|
772
|
+
For each rule execution from runtime:
|
|
773
|
+
1. Find the matching rule in the static workflow JSON (by ruleSet + rule name)
|
|
774
|
+
2. Compare static props vs runtime props:
|
|
775
|
+
- MATCH: all keys and values identical
|
|
776
|
+
- EXTRA_PROP: runtime has a prop not in static JSON (injected by platform or module)
|
|
777
|
+
- MISSING_PROP: static JSON has a prop not seen at runtime (stripped or defaulted)
|
|
778
|
+
- VALUE_DRIFT: same key, different value (version mismatch or override)
|
|
779
|
+
3. Flag VALUE_DRIFT as HIGH — this means the deployed workflow differs from the JSON file
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
##### Report Format
|
|
783
|
+
|
|
784
|
+
```
|
|
785
|
+
=== Rule Props Audit: ORDER::HD ===
|
|
786
|
+
|
|
787
|
+
Ruleset Rule Prop Static Value Runtime Value Status
|
|
788
|
+
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
789
|
+
OrderCreatedWebhook *.SendWebhookWithDynamicAttributes setting webhook.order.created webhook.order.created MATCH
|
|
790
|
+
ProcessOrder *.SetState status ON_VALIDATION ON_VALIDATION MATCH
|
|
791
|
+
ConfirmShipment *.SendWebhookWithDynamicAttributes setting webhook.ful.shipped webhook.shipped VALUE_DRIFT
|
|
792
|
+
CreateFulfilment *.CreateFulfilmentFromSourcingLocation fulfilmentType HD HD MATCH
|
|
793
|
+
CreateFulfilment *.CreateFulfilmentFromSourcingLocation maxRetries (not in JSON) 3 EXTRA_PROP
|
|
794
|
+
|
|
795
|
+
Summary: 4/5 MATCH, 0 MISSING, 1 VALUE_DRIFT, 1 EXTRA_PROP
|
|
796
|
+
Action: VALUE_DRIFT on "ConfirmShipment" setting prop → re-download workflow or check deployment version
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
##### Severity
|
|
800
|
+
|
|
801
|
+
| Status | Severity | Meaning |
|
|
802
|
+
|--------|----------|---------|
|
|
803
|
+
| MATCH | None | Static and runtime agree |
|
|
804
|
+
| VALUE_DRIFT | HIGH | Deployed workflow differs from local JSON — re-download or re-deploy |
|
|
805
|
+
| MISSING_PROP | MEDIUM | Prop exists in JSON but was not observed — rule may not have fired, or platform stripped it |
|
|
806
|
+
| EXTRA_PROP | LOW | Platform or module injected a default prop not in the JSON — usually harmless |
|
|
807
|
+
|
|
808
|
+
##### Integration
|
|
809
|
+
|
|
810
|
+
- Use runtime props data from `/fluent-trace` Step 8A–8F event extraction
|
|
811
|
+
- Feed VALUE_DRIFT findings into `/fluent-workflow` for re-download
|
|
812
|
+
- Feed MISSING_PROP findings into `/fluent-trace` for investigation of why the rule didn't fire
|
|
813
|
+
|
|
814
|
+
### Step 5: Generate Report
|
|
815
|
+
|
|
816
|
+
Produce a structured analysis report:
|
|
817
|
+
|
|
818
|
+
```
|
|
819
|
+
=== Workflow Analysis: ORDER::HD (v1.50) ===
|
|
820
|
+
|
|
821
|
+
Metadata:
|
|
822
|
+
Entity Type: ORDER
|
|
823
|
+
Entity Subtype: HD
|
|
824
|
+
Retailer ID: 2
|
|
825
|
+
Total Statuses: 29 (ORDER=8, FULFILMENT=11, ARTICLE=5, CONSIGNMENT=5)
|
|
826
|
+
Total Rulesets: 61
|
|
827
|
+
|
|
828
|
+
Status Graph (ORDER):
|
|
829
|
+
CREATED -> ON_VALIDATION -> BOOKED -> PICK_PACK -> AWAITING_COURIER_COLLECTION -> COMPLETE
|
|
830
|
+
-> CANCELLED
|
|
831
|
+
-> ESCALATED
|
|
832
|
+
|
|
833
|
+
Entity Type Breakdown:
|
|
834
|
+
ORDER: 8 statuses, 12 rulesets
|
|
835
|
+
FULFILMENT: 11 statuses, 20 rulesets
|
|
836
|
+
ARTICLE: 5 statuses, 15 rulesets
|
|
837
|
+
CONSIGNMENT: 5 statuses, 14 rulesets
|
|
838
|
+
|
|
839
|
+
Issues Found:
|
|
840
|
+
[WARN] Orphaned ruleset "UpsertEventAttributes" — no trigger, no SendEvent targets it
|
|
841
|
+
[WARN] Dead-end status "ESCALATED" is category DONE — OK (terminal)
|
|
842
|
+
[INFO] 4 cross-entity SendEvent calls (ORDER -> FULFILMENT events)
|
|
843
|
+
|
|
844
|
+
Event Chain Depth:
|
|
845
|
+
Longest chain: CREATE -> ... -> OrderComplete (12 hops)
|
|
846
|
+
Deepest cascade: ConfirmValidation triggers 8 downstream rulesets
|
|
847
|
+
|
|
848
|
+
Rule Module Usage:
|
|
849
|
+
<ACCOUNT>.core: 45 usages (SetState, SendEvent, ScheduleEvent, SendWebhook)
|
|
850
|
+
<ACCOUNT>.order: 38 usages (domain-specific rules)
|
|
851
|
+
<ACCOUNT>.metrics: 20 usages (UpdateStatusHistory, ComputeMetrics)
|
|
852
|
+
|
|
853
|
+
Process Flow Classification:
|
|
854
|
+
CREATE (1): CREATE
|
|
855
|
+
USER_ACTION (3): CancelOrder, ConfirmPick, ConfirmShipment
|
|
856
|
+
INTEGRATION_EVENT (2): PaymentCallback, InventoryUpdate
|
|
857
|
+
SCHEDULED_EVENT (1): ValidationGraceExpired
|
|
858
|
+
CROSS_WORKFLOW (4): CreateFulfilment, NotifyFulfilmentShipped, ...
|
|
859
|
+
INTERNAL (51): ProcessOrder, ConfirmValidation, ...
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
### Step 5A: Process Flow Classification
|
|
863
|
+
|
|
864
|
+
Classify each ruleset by its role in the business process. This reveals how work enters and flows through the system.
|
|
865
|
+
|
|
866
|
+
| Flow Type | Detection Rule | Priority |
|
|
867
|
+
|-----------|---------------|----------|
|
|
868
|
+
| **CREATE** | Ruleset name is exactly `CREATE` | 1 (highest) |
|
|
869
|
+
| **USER_ACTION** | Ruleset has `userActions` defined (UI button triggers) | 2 |
|
|
870
|
+
| **SCHEDULED_EVENT** | Ruleset is the target of a `ScheduleEvent` rule (has delay/schedule props) | 3 |
|
|
871
|
+
| **CROSS_WORKFLOW** | Ruleset receives events from a different workflow type (cross-workflow incoming connection) | 4 |
|
|
872
|
+
| **INTEGRATION_EVENT** | Ruleset has no internal or cross-workflow incoming connections (external API/webhook entry point) | 5 |
|
|
873
|
+
| **INTERNAL** | All other rulesets — triggered internally via `SendEvent` chains or status triggers | 6 (default) |
|
|
874
|
+
|
|
875
|
+
**Classification algorithm:**
|
|
876
|
+
```
|
|
877
|
+
For each ruleset R:
|
|
878
|
+
if R.name == "CREATE" -> CREATE
|
|
879
|
+
else if R.userActions is non-empty -> USER_ACTION
|
|
880
|
+
else if any other ruleset has ScheduleEvent rule with eventName == R.name -> SCHEDULED_EVENT
|
|
881
|
+
else if R has cross-workflow incoming connections -> CROSS_WORKFLOW
|
|
882
|
+
else if R has no incoming connections from within same workflow -> INTEGRATION_EVENT
|
|
883
|
+
else -> INTERNAL
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**Why this matters:**
|
|
887
|
+
- **CREATE** — entity generation rate and initialization quality
|
|
888
|
+
- **USER_ACTION** — manual intervention points (bottleneck candidates)
|
|
889
|
+
- **SCHEDULED_EVENT** — time-dependent automation (monitor for stale timers)
|
|
890
|
+
- **CROSS_WORKFLOW** — inter-workflow dependencies (failure propagation risk)
|
|
891
|
+
- **INTEGRATION_EVENT** — external system entry points (webhook health)
|
|
892
|
+
- **INTERNAL** — core orchestration logic (event chain depth)
|
|
893
|
+
|
|
894
|
+
## Cross-Workflow Connection Analysis
|
|
895
|
+
|
|
896
|
+
### Connection Types
|
|
897
|
+
|
|
898
|
+
| Type | Definition | Detection |
|
|
899
|
+
|------|-----------|-----------|
|
|
900
|
+
| **Internal** | Events within the same workflow (ruleset A -> SendEvent -> ruleset B within ORDER::HD) | SendEvent target entityType matches workflow's own entityType |
|
|
901
|
+
| **Cross-Entity** | Events targeting child entities (ORDER workflow -> SendEvent -> FULFILMENT entity) | SendEvent target entityType differs from workflow's entityType |
|
|
902
|
+
| **Cross-Workflow** | Events bridging workflow types (ORDER::HD -> FULFILMENT::HD_WH) | Target flexType differs from source flexType; creates new execution thread |
|
|
903
|
+
|
|
904
|
+
### Detection Pattern
|
|
905
|
+
|
|
906
|
+
For each SendEvent rule in the workflow:
|
|
907
|
+
1. Extract target `entityType` from rule props or event attributes
|
|
908
|
+
2. Compare with the workflow's own `entityType`
|
|
909
|
+
3. If same -> **internal connection**
|
|
910
|
+
4. If different -> **cross-entity connection**
|
|
911
|
+
5. Look up target workflow by flexType (entityType::subtype) -> if found, map the bridge -> **cross-workflow connection**
|
|
912
|
+
6. If target workflow not found -> potential **orphan connection** (event will get NO_MATCH)
|
|
913
|
+
|
|
914
|
+
### Cross-Workflow Bridge Report
|
|
915
|
+
|
|
916
|
+
When analyzing workflows, report bridges between them:
|
|
917
|
+
|
|
918
|
+
```
|
|
919
|
+
ORDER::HD -> FULFILMENT::HD_WH
|
|
920
|
+
Bridge event: CreateFulfilment (sent by CreateFulfilmentFromSourcingLocation rule)
|
|
921
|
+
Source ruleset: ProcessHDFulfilmentChoice
|
|
922
|
+
Target workflow: FULFILMENT::HD_WH (CREATE ruleset)
|
|
923
|
+
Thread boundary: YES (different execution context)
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
## Deep Analysis Mode (--deep)
|
|
927
|
+
|
|
928
|
+
When `--deep` is specified, additionally:
|
|
929
|
+
|
|
930
|
+
1. **Cross-workflow analysis** — Download ALL workflows for the retailer and check for cross-entity event consistency (e.g., ORDER workflow sends events that FULFILMENT workflow expects)
|
|
931
|
+
2. **Rule module inventory** — List all unique rule names used, grouped by module
|
|
932
|
+
3. **User action audit** — List all rulesets with `userActions` defined, map to UI button labels
|
|
933
|
+
4. **Attribute flow** — Track attributes set by rules across the event chain
|
|
934
|
+
5. **Webhook mapping** — List all SendWebhook rules and their target settings
|
|
935
|
+
|
|
936
|
+
## Integration with Other Skills
|
|
937
|
+
|
|
938
|
+
| Task | Skill / Tool |
|
|
939
|
+
|------|-------------|
|
|
940
|
+
| Download workflow to analyze | `/fluent-workflow` |
|
|
941
|
+
| Connection-centric dependency mapping | `/fluent-connection-analysis` |
|
|
942
|
+
| Understand what a rule does (live registry) | `plugin.list` MCP tool |
|
|
943
|
+
| Read custom rule source code | `/fluent-custom-code` |
|
|
944
|
+
| Decompile reference module JARs | `/fluent-custom-code` or manual `cfr`/`procyon` |
|
|
945
|
+
| Fix issues found by analyzer | `/fluent-workflow-builder` |
|
|
946
|
+
| Deploy fixed workflow | `/fluent-module-deploy` or `/fluent-workflow` |
|
|
947
|
+
| Test after deployment | `/fluent-e2e-test` |
|
|
948
|
+
| Trace runtime failures | `/fluent-trace` |
|
|
949
|
+
| Query available user actions | `/fluent-transition-api` |
|
|
950
|
+
| Check rule settings dependencies | `/fluent-settings` |
|
|
951
|
+
|
|
952
|
+
## Tips
|
|
953
|
+
|
|
954
|
+
- **Always download the latest version** before analyzing. Prefer MCP-based download (`workflow.list` then `workflow.download` for each, saving with `::` replaced by `__` in filenames). On macOS/Linux, `fluent workflow download -w all` also works. On Windows, the CLI fails on `::` filenames — use `--json` export and split to sanitized filenames with `workflow-file-map.json`.
|
|
955
|
+
- **Composite workflows** contain multiple entity types (ORDER + FULFILMENT + ARTICLE + CONSIGNMENT) — analyze each entity type's status graph separately
|
|
956
|
+
- **SendEvent across entity types** is normal — ORDER rulesets often send events to FULFILMENT entities and vice versa
|
|
957
|
+
- **Rule names reveal module ownership** — `<ACCOUNT>.core.*` are platform module rules, `<ACCOUNT>.order.*` are from the order module, custom modules use their own prefix
|
|
958
|
+
- **Gate rulesets** (e.g., `SendEventOnVerifyingAllFcStatus`) wait for child entities before advancing parent — trace these carefully
|
|
959
|
+
- **Never guess what a rule does** — always use `plugin.list` first, then source/decompiled code if you need implementation details
|