@fluentcommerce/ai-skills 0.1.0 → 0.3.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.

Potentially problematic release.


This version of @fluentcommerce/ai-skills might be problematic. Click here for more details.

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