@fluentcommerce/ai-skills 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +622 -0
- package/bin/cli.mjs +1973 -0
- package/content/cli/agents/fluent-cli/agent.json +149 -0
- package/content/cli/agents/fluent-cli.md +132 -0
- package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
- package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
- package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
- package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
- package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
- package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
- package/content/cli/skills/fluent-connect/SKILL.md +886 -0
- package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
- package/content/cli/skills/fluent-profile/SKILL.md +180 -0
- package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
- package/content/dev/agents/fluent-dev/agent.json +88 -0
- package/content/dev/agents/fluent-dev.md +525 -0
- package/content/dev/reference-modules/catalog.json +4754 -0
- package/content/dev/skills/fluent-build/SKILL.md +192 -0
- package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
- package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
- package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
- package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
- package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
- package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
- package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
- package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
- package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
- package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
- package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
- package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
- package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
- package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
- package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
- package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
- package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
- package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
- package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
- package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
- package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
- package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
- package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
- package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
- package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
- package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
- package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
- package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
- package/content/mcp-extn/agents/fluent-mcp.md +69 -0
- package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
- package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
- package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
- package/content/rfl/agents/fluent-rfl.md +56 -0
- package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
- package/docs/CAPABILITY_MAP.md +77 -0
- package/docs/CLI_COVERAGE.md +47 -0
- package/docs/DEV_WORKFLOW.md +802 -0
- package/docs/FLOW_RUN.md +142 -0
- package/docs/USE_CASES.md +404 -0
- package/metadata.json +156 -0
- package/package.json +51 -0
|
@@ -0,0 +1,1143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fluent-trace
|
|
3
|
+
description: Trace and debug Fluent Commerce event processing. Analyze failed events, correlate with workflows and rules, identify root causes. Triggers on "trace event", "debug order", "why did event fail", "event trace", "debug fulfilment".
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
|
|
6
|
+
argument-hint: <entity-ref> [--entity-type ORDER|FULFILMENT] [--status FAILED]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Event Trace & Debug
|
|
10
|
+
|
|
11
|
+
Trace Fluent Commerce event processing to diagnose failures, identify root causes, and correlate events with workflow rulesets and rules.
|
|
12
|
+
|
|
13
|
+
## Ownership Boundary
|
|
14
|
+
|
|
15
|
+
This skill owns diagnostic workflow, correlation logic, and decision trees.
|
|
16
|
+
|
|
17
|
+
Canonical MCP extension tool syntax/limits are owned by `/fluent-mcp-tools`. Reuse that skill for exact request payload patterns.
|
|
18
|
+
|
|
19
|
+
## Pre-Check: Load Source Analysis
|
|
20
|
+
|
|
21
|
+
Before tracing, check if `/fluent-custom-code` analysis artifacts exist:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
accounts/<PROFILE>/analysis/custom-code/workflow-rule-map.json
|
|
25
|
+
accounts/<PROFILE>/analysis/custom-code/source-map.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If found, use them to:
|
|
29
|
+
- Map rule names in event traces to actual source classes and file paths
|
|
30
|
+
- Understand rule parameters and expected behavior
|
|
31
|
+
- Identify confidence level of rule mappings (exact vs inferred)
|
|
32
|
+
- Jump directly to source when a rule fails
|
|
33
|
+
|
|
34
|
+
If not found, fall back to workflow JSON analysis and runtime evidence only. Treat artifact-gate failures as "not found" (for example missing required files, missing hashes, or blocking `missingSources` in `constraints.json`).
|
|
35
|
+
|
|
36
|
+
## When to Use
|
|
37
|
+
|
|
38
|
+
- An event was sent but the entity didn't change state
|
|
39
|
+
- Event processing returned FAILED status
|
|
40
|
+
- Need to understand why an order/fulfilment is stuck
|
|
41
|
+
- Investigating which ruleset processed (or didn't process) an event
|
|
42
|
+
- Debugging rule execution errors
|
|
43
|
+
|
|
44
|
+
### Handoff from `/fluent-e2e-test`
|
|
45
|
+
|
|
46
|
+
When invoked after a failed E2E step, accept and reuse the handoff payload when provided:
|
|
47
|
+
- `entityRef`, `entityType`
|
|
48
|
+
- `eventId` (optional)
|
|
49
|
+
- expected vs actual status
|
|
50
|
+
|
|
51
|
+
If `eventId` is already known, go straight to `event.get` and avoid redundant listing.
|
|
52
|
+
|
|
53
|
+
### API Constraints to Remember
|
|
54
|
+
|
|
55
|
+
- **Archival window:** Events are retained for ~4 months. Queries beyond this return empty results.
|
|
56
|
+
- **Rate limiting:** ~100 requests/minute per user. When paginating large audit trails, add brief pauses between calls.
|
|
57
|
+
- **Max count:** MCP `event.list` caps at 500 per request; REST API supports up to 5,000 but may timeout.
|
|
58
|
+
- **Recommended time range:** 5 minutes per query for high-volume entities. Use narrow `from`/`to` windows.
|
|
59
|
+
|
|
60
|
+
## Quick Start: One-Call Forensics
|
|
61
|
+
|
|
62
|
+
For most debugging scenarios, start with `event.flowInspect`:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
event.flowInspect({
|
|
66
|
+
rootEntityRef: "<ORDER_REF>",
|
|
67
|
+
rootEntityType: "ORDER"
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Compact mode (default):** Returns a pre-analyzed summary (~2-3k tokens) with an `analysis` section containing `statusFlow`, `findings` (anomaly detection), `failedWebhookEndpoints`, and `slowestRulesets`. Audit arrays are stripped to essentials — only failed webhooks, top 5 mutations by name (count only), and top 5 slowest rulesets. Diagnostics show summary inspected events (id, name, status, closeMatch count).
|
|
72
|
+
|
|
73
|
+
**IMPORTANT — Context budget discipline:**
|
|
74
|
+
- ALWAYS start with compact mode (the default). The ~2-3k token summary is sufficient for 90% of debugging.
|
|
75
|
+
- NEVER call `compact: false` as a first step — it returns ~24-30k tokens and fills context.
|
|
76
|
+
- If compact findings point to a specific issue, drill down with ONE targeted flag at a time (keep `compact: true`):
|
|
77
|
+
- Rule timing issue → add `includeRuleDetails: true`
|
|
78
|
+
- Need custom log messages → add `includeCustomLogs: true`
|
|
79
|
+
- Need entity state snapshots → add `includeSnapshots: true`
|
|
80
|
+
- Need child entity events → add `includeCrossEntity: true`
|
|
81
|
+
- Only use `compact: false` as a last resort when you need raw arrays and have already identified what to look for.
|
|
82
|
+
|
|
83
|
+
Example (targeted drill-down for rule timing — keeps compact summary, adds rule details):
|
|
84
|
+
```
|
|
85
|
+
event.flowInspect({
|
|
86
|
+
rootEntityRef: "<ORDER_REF>",
|
|
87
|
+
rootEntityType: "ORDER",
|
|
88
|
+
includeRuleDetails: true
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Response sections (compact mode):**
|
|
93
|
+
- `totals` — raw event counts (always present)
|
|
94
|
+
- `analysis` — local findings: `statusFlow`, `entityTypes`, `timespan`, `failedWebhookEndpoints`, `slowestRulesets`, `findings[]`
|
|
95
|
+
- `orchestration` — statusCounts, entityTypeCounts, topNames (top 10, no events array)
|
|
96
|
+
- `audit.exceptions` — rule exceptions (critical, always included)
|
|
97
|
+
- `audit.webhookActions` — only failures (responseCode >= 400)
|
|
98
|
+
- `audit.mutationActions` — top 5 by queryName (name + count only)
|
|
99
|
+
- `audit.sendEventActions` — count + scheduledCount only
|
|
100
|
+
- `diagnostics` — summary inspected events (no full event payload)
|
|
101
|
+
|
|
102
|
+
**Additional sections (available via targeted flags, all work with compact: true):**
|
|
103
|
+
- `audit.ruleEvents` — per-rule execution details (add `includeRuleDetails: true`)
|
|
104
|
+
- `audit.customLogs` — custom plugin logs (add `includeCustomLogs: true`)
|
|
105
|
+
- `audit.snapshots` — entity snapshots (add `includeSnapshots: true`)
|
|
106
|
+
- `diagnostics.noMatchAuditDetails` — closeMatches from ruleSet audit (add `includeNoMatchDetails: true`, on by default)
|
|
107
|
+
- `crossEntity` — child entity events (add `includeCrossEntity: true`)
|
|
108
|
+
|
|
109
|
+
See `/fluent-mcp-tools` for full parameter reference.
|
|
110
|
+
|
|
111
|
+
Use the manual step-by-step workflow below when you need finer control over individual queries or when investigating a specific event ID.
|
|
112
|
+
|
|
113
|
+
## Diagnostic Workflow (Manual)
|
|
114
|
+
|
|
115
|
+
### Step 1: Get Entity Current State
|
|
116
|
+
|
|
117
|
+
Use MCP `graphql.query` to fetch the entity's current status and key attributes:
|
|
118
|
+
|
|
119
|
+
**For an ORDER:**
|
|
120
|
+
```graphql
|
|
121
|
+
{
|
|
122
|
+
orders(ref: ["<ORDER_REF>"], first: 1) {
|
|
123
|
+
edges {
|
|
124
|
+
node {
|
|
125
|
+
id
|
|
126
|
+
ref
|
|
127
|
+
type
|
|
128
|
+
status
|
|
129
|
+
createdOn
|
|
130
|
+
updatedOn
|
|
131
|
+
attributes {
|
|
132
|
+
name
|
|
133
|
+
type
|
|
134
|
+
value
|
|
135
|
+
}
|
|
136
|
+
fulfilmentChoice {
|
|
137
|
+
edges {
|
|
138
|
+
node {
|
|
139
|
+
id
|
|
140
|
+
ref
|
|
141
|
+
type
|
|
142
|
+
status
|
|
143
|
+
deliveryType
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
items {
|
|
148
|
+
edges {
|
|
149
|
+
node {
|
|
150
|
+
ref
|
|
151
|
+
quantity
|
|
152
|
+
status
|
|
153
|
+
fulfilmentChoiceRef
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**For a FULFILMENT:**
|
|
164
|
+
```graphql
|
|
165
|
+
{
|
|
166
|
+
fulfilments(ref: ["<FULFILMENT_REF>"], first: 1) {
|
|
167
|
+
edges {
|
|
168
|
+
node {
|
|
169
|
+
id
|
|
170
|
+
ref
|
|
171
|
+
type
|
|
172
|
+
status
|
|
173
|
+
createdOn
|
|
174
|
+
updatedOn
|
|
175
|
+
attributes {
|
|
176
|
+
name
|
|
177
|
+
type
|
|
178
|
+
value
|
|
179
|
+
}
|
|
180
|
+
items {
|
|
181
|
+
edges {
|
|
182
|
+
node {
|
|
183
|
+
ref
|
|
184
|
+
status
|
|
185
|
+
requestedQuantity
|
|
186
|
+
filledQuantity
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
order {
|
|
191
|
+
id
|
|
192
|
+
ref
|
|
193
|
+
status
|
|
194
|
+
}
|
|
195
|
+
fromLocation {
|
|
196
|
+
ref
|
|
197
|
+
name
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Step 2: Fetch Event History
|
|
206
|
+
|
|
207
|
+
Use MCP `event.list` to get all events for this entity:
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
event.list({
|
|
211
|
+
"context.entityRef": "<ENTITY_REF>",
|
|
212
|
+
"context.entityType": "ORDER",
|
|
213
|
+
"count": 50
|
|
214
|
+
})
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Filter by failed events:**
|
|
218
|
+
```
|
|
219
|
+
event.list({
|
|
220
|
+
"context.entityRef": "<ENTITY_REF>",
|
|
221
|
+
"context.entityType": "ORDER",
|
|
222
|
+
"eventStatus": "FAILED",
|
|
223
|
+
"count": 50
|
|
224
|
+
})
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Filter by time window:**
|
|
228
|
+
```
|
|
229
|
+
event.list({
|
|
230
|
+
"context.entityRef": "<ENTITY_REF>",
|
|
231
|
+
"from": "2026-02-20T00:00:00Z",
|
|
232
|
+
"to": "2026-02-20T23:59:59Z",
|
|
233
|
+
"count": 100
|
|
234
|
+
})
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
For full event-model/filter semantics and causality patterns (`context.sourceEvents`, root-entity tracing), use `/fluent-event-api`.
|
|
238
|
+
|
|
239
|
+
### Step 3: Inspect Failed Event
|
|
240
|
+
|
|
241
|
+
Use MCP `event.get` with the event ID from step 2:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
event.get({ "eventId": "<EVENT_ID>" })
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Look for:
|
|
248
|
+
- `status` — FAILED, PENDING, SUCCESS
|
|
249
|
+
- `response.errors` — Error messages from rule execution
|
|
250
|
+
- `context.entityType` + `context.entityRef` — Which entity was targeted
|
|
251
|
+
- `context.rootEntityType` + `context.rootEntityRef` — Parent entity context
|
|
252
|
+
- `attributes` — What data was sent with the event
|
|
253
|
+
|
|
254
|
+
### Step 4: Correlate with Workflow
|
|
255
|
+
|
|
256
|
+
Given the event name and entity status at time of event, identify which ruleset should have processed it:
|
|
257
|
+
|
|
258
|
+
1. **Download the workflow** via MCP `workflow.download` or read local copy
|
|
259
|
+
2. **Find the matching ruleset:**
|
|
260
|
+
- If the event name matches a ruleset name → that ruleset should fire
|
|
261
|
+
- If the entity just entered a new status → find ruleset with `triggers: [{ "status": "<STATUS>" }]`
|
|
262
|
+
3. **Check the rules** in that ruleset — identify which rule likely failed
|
|
263
|
+
|
|
264
|
+
### Step 5: Identify Root Cause
|
|
265
|
+
|
|
266
|
+
Common failure patterns:
|
|
267
|
+
|
|
268
|
+
| Symptom | Likely Cause | How to Verify |
|
|
269
|
+
|---------|-------------|---------------|
|
|
270
|
+
| Event FAILED, no state change | Rule threw exception | Check event response errors |
|
|
271
|
+
| Event SUCCESS but no state change | Rule ran but SetState was conditional | Check gate conditions, verify child entity statuses |
|
|
272
|
+
| Event PENDING for a long time | Async processing queue backlog | Wait and re-check, or send again |
|
|
273
|
+
| Event NO_MATCH | No ruleset matched event name or status trigger | Verify event name matches a ruleset name in the workflow; check entity subtype matches workflow flexType |
|
|
274
|
+
| Event SUCCESS but wrong state | Ruleset executed unexpected rules | Check trigger status matches expected |
|
|
275
|
+
| Entity stuck in intermediate state | Gate condition not met | Query all child entities (FCs, fulfilments) and check their statuses |
|
|
276
|
+
| Event not found at all | Event sent to wrong entity/ref | Verify entityRef, entityType, retailerId |
|
|
277
|
+
| retailerId mismatch | Event sent with wrong retailerId | Check user scope — account-level users get retailerId=0 |
|
|
278
|
+
|
|
279
|
+
### Step 5B: Causal Chain Reconstruction
|
|
280
|
+
|
|
281
|
+
> **Shortcut:** `event.flowInspect` with `includeRuleDetails: true` returns the full audit chain (ruleSet durations, rule-level execution with props/timing, ACTION payloads) in one call. Use the manual approach below only when you need per-event-ID granularity.
|
|
282
|
+
|
|
283
|
+
When `sourceEvents` is sparse or missing, reconstruct the causal chain using correlation:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
1. Get the ORCHESTRATION event that was sent:
|
|
287
|
+
event.get({ eventId: "<EVENT_ID>" })
|
|
288
|
+
|
|
289
|
+
2. Find all audit events it spawned (time-window correlation):
|
|
290
|
+
event.list({
|
|
291
|
+
eventType: "ORCHESTRATION_AUDIT",
|
|
292
|
+
"context.entityRef": "<ENTITY_REF>",
|
|
293
|
+
from: "<event_generatedOn_minus_1s>",
|
|
294
|
+
to: "<event_generatedOn_plus_30s>",
|
|
295
|
+
count: 100
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
3. Group audit events by category:
|
|
299
|
+
- category=ruleSet → ruleset start/stop (check startTimer/stopTimer)
|
|
300
|
+
- category=rule → individual rule execution
|
|
301
|
+
- category=ACTION → rule actions (SendEvent, SetState, Send Webhook)
|
|
302
|
+
- category=exception → failure point
|
|
303
|
+
|
|
304
|
+
4. Build execution timeline:
|
|
305
|
+
ORCHESTRATION event → ruleSet audit → rule audits → ACTION audits → next ORCHESTRATION (if SendEvent)
|
|
306
|
+
|
|
307
|
+
5. Identify the break point:
|
|
308
|
+
- Last successful rule before exception
|
|
309
|
+
- Missing expected ACTION (e.g., no SetState audit = state didn't change)
|
|
310
|
+
- ORCHESTRATION_AUDIT with category=exception = the failure
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**When sourceEvents IS available:** Follow `sourceEvents[0]` upward to find the parent event. Repeat until you reach an event with empty `sourceEvents` (the root cause event).
|
|
314
|
+
|
|
315
|
+
### Step 5C: Correlation Confidence Ladder
|
|
316
|
+
|
|
317
|
+
When multiple candidate links exist, rank correlation confidence in this order:
|
|
318
|
+
|
|
319
|
+
1. **Direct reference (highest confidence, score ~0.80)**
|
|
320
|
+
Child event `context.sourceEvents[]` contains the parent event ID.
|
|
321
|
+
2. **Entity + timing match (medium confidence, score ~0.15)**
|
|
322
|
+
Same `entityType` + same entity identifier (`entityId`/`entityRef`) within **1ms timing gap**.
|
|
323
|
+
3. **Root-context + sequence match (medium-low confidence, score ~0.05)**
|
|
324
|
+
Same `rootEntityType/rootEntityRef` and expected sequence (`ruleSet` -> `rule` -> `ACTION`).
|
|
325
|
+
4. **Name-only inference (low confidence, score 0)**
|
|
326
|
+
Event/ruleset names suggest linkage but no causal pointer.
|
|
327
|
+
|
|
328
|
+
**Scoring:** When scoring multiple candidates, sum applicable factors (max 1.0). Direct reference (0.80) + entity match (0.15) + timing proximity within 100μs (0.05) = 1.0 (perfect). Use this to pick the best match when multiple candidates exist.
|
|
329
|
+
|
|
330
|
+
**Timing threshold:** Use **1 millisecond** as the maximum gap for timing-based correlation. Events further apart than 1ms on the same entity are unlikely to be causally linked via the same rule execution.
|
|
331
|
+
|
|
332
|
+
Always tag conclusions as **confirmed** (1) or **inferred** (2-4), and include the evidence used.
|
|
333
|
+
|
|
334
|
+
For full event model and filter semantics → `/fluent-event-api`.
|
|
335
|
+
|
|
336
|
+
### Step 5D: Payload Evidence Bundle (Mutations, Webhooks, Scheduled)
|
|
337
|
+
|
|
338
|
+
> **Shortcut:** `event.flowInspect` extracts all three categories below automatically: `audit.mutationActions`, `audit.webhookActions`, `audit.sendEventActions` / `audit.scheduledActions`. Use the manual approach below only for targeted queries outside the root-entity scope.
|
|
339
|
+
|
|
340
|
+
After reconstructing the chain, extract payload evidence from `category=ACTION` events:
|
|
341
|
+
|
|
342
|
+
1. Query:
|
|
343
|
+
```
|
|
344
|
+
event.list({
|
|
345
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
346
|
+
"category": "ACTION",
|
|
347
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
348
|
+
"count": 200
|
|
349
|
+
})
|
|
350
|
+
```
|
|
351
|
+
2. Mutation extraction (often from ACTION events with missing `name`):
|
|
352
|
+
- `attributes.request.queryType` (look for `DynamicUpdateMutation`)
|
|
353
|
+
- `attributes.request.queryName`
|
|
354
|
+
- `attributes.request.queryString`
|
|
355
|
+
- `attributes.request.variables.values.input` (actual mutation payload)
|
|
356
|
+
- `attributes.response.inner`
|
|
357
|
+
3. Webhook extraction:
|
|
358
|
+
- `Request Endpoint`, `Request Headers`
|
|
359
|
+
- `Response code`, `Response reason`, `Response Body`, `Response Headers`
|
|
360
|
+
4. Scheduled/future-dated extraction:
|
|
361
|
+
- `attributes["Future Dated"]`
|
|
362
|
+
- `attributes.Event.scheduledOn`
|
|
363
|
+
- `event.get(eventId).event.meta.scheduledOn`
|
|
364
|
+
5. Diagnose event-status mismatches:
|
|
365
|
+
- If `ORCHESTRATION` shows `PENDING` but downstream ACTION/ruleSet audit completed, treat audit chain as runtime truth and flag status-lag behavior.
|
|
366
|
+
|
|
367
|
+
### Step 6: Deep Dive — Rule-Level Analysis
|
|
368
|
+
|
|
369
|
+
### Step 6A: Evidence Escalation When Logic Is Vague
|
|
370
|
+
|
|
371
|
+
Do not rely on ruleset descriptions alone when rule intent is unclear.
|
|
372
|
+
Escalate evidence in this order:
|
|
373
|
+
|
|
374
|
+
1. **Ask focused clarification questions** (event name, expected state, actual state, and where it diverged)
|
|
375
|
+
2. **Request source first** (`module.json` + Java rule classes)
|
|
376
|
+
3. **If source is unavailable, request compiled artifacts** (module ZIP/JAR)
|
|
377
|
+
4. **Decompile candidate rule classes** and map behavior back to workflow props and event errors
|
|
378
|
+
5. **Correlate with runtime evidence** (`event.get`, `event.list`, entity GraphQL state)
|
|
379
|
+
6. **Report confidence explicitly** (confirmed vs inferred behavior)
|
|
380
|
+
|
|
381
|
+
Use this escalation whenever:
|
|
382
|
+
- rule/ruleset descriptions are generic ("validation", "process", "update")
|
|
383
|
+
- multiple rules can match the same symptom
|
|
384
|
+
- event error output does not directly identify the failing line of logic
|
|
385
|
+
|
|
386
|
+
### Step 6B: Rule Source Analysis
|
|
387
|
+
|
|
388
|
+
If you identified the failing rule, check its source code:
|
|
389
|
+
|
|
390
|
+
1. **Find the rule class** — Map rule name to Java file:
|
|
391
|
+
- `ACCT.MODULE.RuleName` → `rule/<package>/RuleName.java`
|
|
392
|
+
2. **Check required props** — Does the workflow supply all props the rule expects?
|
|
393
|
+
- `@ParamString(name = "xxx")` annotations define expected props
|
|
394
|
+
- Props are CASE-SENSITIVE
|
|
395
|
+
3. **Check dynamic query paths** — If rule uses `DynamicUtils.query()`, are the paths valid?
|
|
396
|
+
4. **Check mutation types** — If rule uses `DynamicUtils.mutate()`, is the input type correct?
|
|
397
|
+
5. **Check setting references** — If rule reads a Setting, does the Setting exist with correct data?
|
|
398
|
+
- Query via MCP: `graphql.query` with `settings(first: 1, context: "RETAILER", contextId: <RETAILER_ID>, name: ["<setting-name>"])`
|
|
399
|
+
|
|
400
|
+
### Step 6C: Decompiled Analysis (No Source Case)
|
|
401
|
+
|
|
402
|
+
**Prerequisite:** Check if decompiled output exists at `accounts/<PROFILE>/SOURCE/.decompiled/`. If empty or missing:
|
|
403
|
+
- Run `/fluent-custom-code <PROFILE>` — it will decompile any JARs found under `SOURCE/`
|
|
404
|
+
- If no JARs are available either, ask the user:
|
|
405
|
+
> "To analyze this rule's implementation, I need the module JAR. You can:
|
|
406
|
+
> 1. Copy the JAR to `accounts/<PROFILE>/SOURCE/` and re-run `/fluent-custom-code <PROFILE>`
|
|
407
|
+
> 2. Run `/fluent-connect` which will guide you through full workspace preparation including JAR decompilation
|
|
408
|
+
> 3. Continue with `plugin.list` data only (rule description and parameters, no implementation detail)"
|
|
409
|
+
- For reference modules (core, order, fulfilment, commonv2, globalinventory, reporting), the JAR can typically be obtained from the Fluent module distribution or build artifacts
|
|
410
|
+
|
|
411
|
+
When only JAR/ZIP artifacts are available:
|
|
412
|
+
|
|
413
|
+
1. Extract class names and identify likely rule classes from `module.json` rule mappings.
|
|
414
|
+
2. Decompile only the suspected classes first (avoid whole-jar noise).
|
|
415
|
+
3. Confirm:
|
|
416
|
+
- which props are read
|
|
417
|
+
- what GraphQL mutations/queries/events are invoked
|
|
418
|
+
- what status transitions or guards are enforced
|
|
419
|
+
4. Reconcile decompiled findings with workflow ruleset props and observed event failures.
|
|
420
|
+
5. Mark any uncertain interpretation (obfuscation/inlined code) before recommending fixes.
|
|
421
|
+
6. Use `accounts/<PROFILE>/SOURCE/.decompiled/<artifact-name>/` and write `DECOMPILED.md` with source artifact path + decompiler used.
|
|
422
|
+
7. Treat JAR/ZIP artifacts and `.decompiled/` output as read-only evidence. Do not recommend editing decompiled code; recommend source onboarding for implementation changes.
|
|
423
|
+
|
|
424
|
+
### Step 6D: Performance Bottleneck Detection
|
|
425
|
+
|
|
426
|
+
When investigating slow processing (not failures), calculate execution duration per step from event timestamps:
|
|
427
|
+
|
|
428
|
+
1. **Collect timing data** — For each event in the causal chain, extract `eventTime` (ISO-8601 with microsecond precision when available).
|
|
429
|
+
2. **Calculate step durations** — Duration between consecutive events in the chain (parent → child).
|
|
430
|
+
3. **Identify bottlenecks** — Flag any step whose duration exceeds **2× the average** step duration.
|
|
431
|
+
4. **Classify severity**:
|
|
432
|
+
- Processing time <1s = normal
|
|
433
|
+
- Processing time 1–5s = slow (investigate rule complexity, external calls)
|
|
434
|
+
- Processing time >5s = critical bottleneck (likely external system wait: webhook, inventory query, payment gateway)
|
|
435
|
+
5. **Report** — List steps ranked by duration, flag bottlenecks, and correlate with rule source if available.
|
|
436
|
+
|
|
437
|
+
### Step 7: Workflow Coverage Analysis (Static vs Dynamic)
|
|
438
|
+
|
|
439
|
+
After tracing an entity's events (Steps 1–6), overlay the trace against the workflow definition to find divergence — rulesets that should have fired but didn't, or unexpected rulesets that appeared.
|
|
440
|
+
|
|
441
|
+
#### When to Use
|
|
442
|
+
|
|
443
|
+
- After tracing an entity that's stuck or behaved unexpectedly
|
|
444
|
+
- After E2E test pass/fail to verify the full expected path was executed
|
|
445
|
+
- During go-live readiness to confirm workflow coverage on real orders
|
|
446
|
+
|
|
447
|
+
#### Coverage Algorithm
|
|
448
|
+
|
|
449
|
+
1. **Reconstruct the entity's status path** from the event trace (Step 2 data):
|
|
450
|
+
```
|
|
451
|
+
Extract all status transitions from events:
|
|
452
|
+
CREATED → ON_VALIDATION → BOOKED → PICK_PACK → COMPLETE
|
|
453
|
+
Record which event name triggered each transition.
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
2. **Load the workflow definition** (local file or `workflow.download`):
|
|
457
|
+
```
|
|
458
|
+
Parse all rulesets for this entity type.
|
|
459
|
+
For each status in the entity's actual path, collect:
|
|
460
|
+
a. Status-triggered rulesets (triggers[].status matches the status)
|
|
461
|
+
b. Event-name-triggered rulesets (name matches a SendEvent from the previous ruleset's rules)
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
3. **Build expected ruleset sequence** from static analysis:
|
|
465
|
+
```
|
|
466
|
+
Status: CREATED
|
|
467
|
+
Expected: CREATE ruleset → rules: [CreateVariants, UpdateStatusHistory, SendWebhook, SendEvent(ValidateOrder)]
|
|
468
|
+
Expected downstream: ValidateOrder ruleset
|
|
469
|
+
|
|
470
|
+
Status: ON_VALIDATION
|
|
471
|
+
Expected: status-triggered rulesets + event-triggered rulesets from previous step's SendEvents
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
4. **Compare expected vs actual**:
|
|
475
|
+
```
|
|
476
|
+
For each expected ruleset:
|
|
477
|
+
Search event trace for ORCHESTRATION_AUDIT with category=ruleSet matching this name
|
|
478
|
+
If found → MATCHED
|
|
479
|
+
If not found → MISSED
|
|
480
|
+
|
|
481
|
+
For each ruleset in the event trace:
|
|
482
|
+
Check if it appears in the expected sequence
|
|
483
|
+
If not → UNEXPECTED
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
5. **Analyze missed rulesets** — for each MISSED ruleset, determine why:
|
|
487
|
+
- Triggering event never sent → upstream rule didn't fire `SendEvent`
|
|
488
|
+
- Status trigger never reached → entity took a different branch
|
|
489
|
+
- Conditional rule (ForwardIf*, gate) evaluated to false → check condition props
|
|
490
|
+
- Setting missing caused the rule to skip → cross-reference with `/fluent-settings` audit
|
|
491
|
+
- Entity subtype mismatch → ruleset scoped to a different flexType
|
|
492
|
+
|
|
493
|
+
#### Coverage Report Format
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
=== Workflow Coverage: ORDER ref=HD-20260222-001 ===
|
|
497
|
+
|
|
498
|
+
Status Path: CREATED → ON_VALIDATION → BOOKED → PICK_PACK → COMPLETE
|
|
499
|
+
|
|
500
|
+
Expected Rulesets Actual (from trace) Status
|
|
501
|
+
──────────────────────────────────────────────────────────────
|
|
502
|
+
CREATE CREATE MATCHED
|
|
503
|
+
ValidateOrder ValidateOrder MATCHED
|
|
504
|
+
ConfirmValidation ConfirmValidation MATCHED
|
|
505
|
+
ProcessOrder ProcessOrder MATCHED
|
|
506
|
+
BookOrder BookOrder MATCHED
|
|
507
|
+
NotifyOrderBooked (not found) MISSED → setting "webhook.order.booked" not found
|
|
508
|
+
ConfirmPick ConfirmPick MATCHED
|
|
509
|
+
ConfirmShipment ConfirmShipment MATCHED
|
|
510
|
+
|
|
511
|
+
Unexpected:
|
|
512
|
+
RetryValidation → fired at ON_VALIDATION (ScheduleEvent timeout handler)
|
|
513
|
+
|
|
514
|
+
Coverage: 7/8 expected rulesets matched (87.5%)
|
|
515
|
+
Missed: 1 (setting dependency — fixable via /fluent-settings)
|
|
516
|
+
Unexpected: 1 (timeout handler — normal for slow processing)
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
#### Interpreting Results
|
|
520
|
+
|
|
521
|
+
| Result | Meaning | Action |
|
|
522
|
+
|--------|---------|--------|
|
|
523
|
+
| **High coverage (>90%)** | Workflow executing as designed | No action needed |
|
|
524
|
+
| **Missed rulesets with setting cause** | Settings gap — rule skipped silently | Run `/fluent-settings --audit` and create missing settings |
|
|
525
|
+
| **Missed rulesets with condition cause** | Conditional branch not taken | Verify entity data meets the condition (attributes, child statuses) |
|
|
526
|
+
| **Unexpected rulesets** | Timer/retry handlers or misconfigured triggers | Check if ruleset is a ScheduleEvent target (normal) or a trigger conflict (bug) |
|
|
527
|
+
| **Low coverage (<70%)** | Workflow path fundamentally different from expected | Re-examine entity subtype, workflow version, and event sequence |
|
|
528
|
+
|
|
529
|
+
#### Integration
|
|
530
|
+
|
|
531
|
+
- Use `/fluent-workflow-analyzer` output for the expected ruleset sequence (static analysis)
|
|
532
|
+
- Use Steps 1–5 event trace data for actual execution evidence (dynamic analysis)
|
|
533
|
+
- Use `/fluent-settings --audit` to explain missing-setting causes for MISSED rulesets
|
|
534
|
+
- Hand off MISSED rulesets to `/fluent-settings` for remediation or `/fluent-workflow-builder` for workflow fixes
|
|
535
|
+
|
|
536
|
+
## Detailed Event Data Extraction
|
|
537
|
+
|
|
538
|
+
After diagnosing an issue or completing an E2E test, extract specific data types from the event audit trail for comprehensive analysis. These sections go beyond root-cause diagnosis — they produce structured inventories of what the orchestration engine actually did.
|
|
539
|
+
|
|
540
|
+
> **One-call alternative:** Use `event.flowInspect` to extract all sections below (8A–8F) in a single API call. It returns mutations, webhooks, scheduled dispatches, NO_MATCH diagnostics with closeMatches, custom rule logs, entity snapshots, rule execution props, and exception details — all keyed by root entity. The manual patterns below remain useful when you need to customize extraction or filter beyond what flowInspect provides.
|
|
541
|
+
|
|
542
|
+
### 8A: Mutation Audit Trail
|
|
543
|
+
|
|
544
|
+
Extract every GraphQL mutation the workflow executed, with the exact query string and input variables. Mutation ACTION events have a **null name** and contain the full mutation payload.
|
|
545
|
+
|
|
546
|
+
#### Query
|
|
547
|
+
|
|
548
|
+
```
|
|
549
|
+
event.list({
|
|
550
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
551
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
552
|
+
"count": 200
|
|
553
|
+
})
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Filter results where `name` is null and `attributes` contains `request.queryString`.
|
|
557
|
+
|
|
558
|
+
#### Data Fields
|
|
559
|
+
|
|
560
|
+
| Attribute | Description |
|
|
561
|
+
|-----------|-------------|
|
|
562
|
+
| `request.queryString` | Full GraphQL mutation string (e.g., `mutation updateFulfilment($input: UpdateFulfilmentInput!) { ... }`) |
|
|
563
|
+
| `request.inputName` | Input type name (e.g., `UpdateFulfilmentInput`) |
|
|
564
|
+
| `request.queryType` | Mutation classification (e.g., `DynamicUpdateMutation`) |
|
|
565
|
+
| `request.variables.values.input` | Exact input payload — entity ID, status changes, attribute updates |
|
|
566
|
+
|
|
567
|
+
#### Report Format
|
|
568
|
+
|
|
569
|
+
```
|
|
570
|
+
=== Mutation Audit Trail: ORDER ref=HD-20260222-001 ===
|
|
571
|
+
|
|
572
|
+
# Entity Mutation Type Target Status Attributes Changed
|
|
573
|
+
─────────────────────────────────────────────────────────────────────────────────
|
|
574
|
+
1 ORDER:143 updateOrder ORDER_SPLIT status
|
|
575
|
+
2 FC:89 updateFulfilmentChoice AWAITING_FC status
|
|
576
|
+
3 FULFILMENT:201 updateFulfilment CREATED status, fromAddress, toAddress, deliveryType
|
|
577
|
+
4 FULFILMENT:201 updateFulfilment RECEIVED status, attributes[allocatedAt]
|
|
578
|
+
5 ORDER:143 updateOrder IN_PROGRESS status
|
|
579
|
+
6 FULFILMENT:201 updateFulfilment PICKPACK status, attributes[pickedAt]
|
|
580
|
+
7 FULFILMENT:201 updateFulfilment SHIPPED status, attributes[trackingNumber, carrierRef]
|
|
581
|
+
|
|
582
|
+
Total: 7 mutations across 3 entity types
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
#### Use Cases
|
|
586
|
+
|
|
587
|
+
- **Audit compliance**: Verify every state change has a corresponding mutation record
|
|
588
|
+
- **Debugging stuck entities**: Identify if a SetState mutation was attempted but failed
|
|
589
|
+
- **Change impact analysis**: See exactly which attributes were modified and in what order
|
|
590
|
+
- **Rule behavior verification**: Confirm a rule is constructing the correct mutation input
|
|
591
|
+
|
|
592
|
+
### 8B: Webhook Delivery Report
|
|
593
|
+
|
|
594
|
+
Extract full webhook request/response round-trips from ACTION events named "Send Webhook". This supplements the existing Webhook Failure Triage section with a comprehensive delivery inventory.
|
|
595
|
+
|
|
596
|
+
#### Query
|
|
597
|
+
|
|
598
|
+
```
|
|
599
|
+
event.list({
|
|
600
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
601
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
602
|
+
"count": 200
|
|
603
|
+
})
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
Filter results where `name == "Send Webhook"`.
|
|
607
|
+
|
|
608
|
+
#### Data Fields
|
|
609
|
+
|
|
610
|
+
| Attribute | Description |
|
|
611
|
+
|-----------|-------------|
|
|
612
|
+
| `Request Endpoint` | Target URL the webhook was sent to |
|
|
613
|
+
| `Request Headers` | HTTP headers sent (may include auth tokens) |
|
|
614
|
+
| `Response code` | HTTP response status (200=OK, 404=not found, 0=timeout) |
|
|
615
|
+
| `Response Body` | Full response body from the target server |
|
|
616
|
+
| `Response Headers` | Response headers (rate-limit, content-type) |
|
|
617
|
+
| `Response reason` | HTTP status text (e.g., "Not Found", "OK") |
|
|
618
|
+
| Dynamic attributes | Rule-injected attributes (e.g., `orderType`, `orderId`, `orderStatus`, `eventName`, `fulfilmentId`) |
|
|
619
|
+
|
|
620
|
+
#### Report Format
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
=== Webhook Delivery Report: ORDER ref=HD-20260222-001 ===
|
|
624
|
+
|
|
625
|
+
# Ruleset Endpoint Status Duration
|
|
626
|
+
────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
627
|
+
1 OrderCreatedWebhook https://api.example.com/order/created 200 OK 120ms
|
|
628
|
+
2 FulfilmentCreatedWebhook https://api.example.com/fulfilment/created 200 OK 95ms
|
|
629
|
+
3 FulfilmentShippedWebhook https://api.example.com/fulfilment/shipped 404 NF 45ms ← FAIL
|
|
630
|
+
4 OrderCompletedWebhook https://api.example.com/order/completed 200 OK 110ms
|
|
631
|
+
|
|
632
|
+
Summary: 3/4 delivered successfully, 1 FAILED (404)
|
|
633
|
+
Failed webhooks: FulfilmentShippedWebhook → check setting "webhook.fulfilment.shipped"
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
#### Dynamic Attributes Extraction
|
|
637
|
+
|
|
638
|
+
Webhook ACTION events also contain dynamic attributes injected by the rule at execution time. These represent the payload data sent alongside the webhook:
|
|
639
|
+
|
|
640
|
+
```
|
|
641
|
+
Dynamic attributes for webhook #1 (OrderCreatedWebhook):
|
|
642
|
+
orderType: HD
|
|
643
|
+
orderId: 143
|
|
644
|
+
orderStatus: CREATED
|
|
645
|
+
eventName: CREATE
|
|
646
|
+
fulfilmentChoiceRef: FC-HD-20260222-001
|
|
647
|
+
sourceSystem: E2E_TEST
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
These dynamic attributes are valuable for:
|
|
651
|
+
- Reconstructing what the external system received
|
|
652
|
+
- Verifying the correct data was included in the webhook payload
|
|
653
|
+
- Debugging webhook receiver failures when the response body is unhelpful
|
|
654
|
+
|
|
655
|
+
#### Cross-Reference with Settings
|
|
656
|
+
|
|
657
|
+
Compare each webhook's `Request Endpoint` with the configured setting value:
|
|
658
|
+
|
|
659
|
+
```
|
|
660
|
+
1. Extract setting name from workflow rule props (e.g., "webhook.order.created")
|
|
661
|
+
2. Query: settings(name: ["webhook.order.created"], context: "RETAILER")
|
|
662
|
+
3. Parse lobValue.url from the setting
|
|
663
|
+
4. Compare with Request Endpoint from the ACTION event
|
|
664
|
+
5. If different → CONTRACT_MISMATCH (setting was changed after the event was processed, or wrong setting was loaded)
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### 8C: Scheduled Event Inventory
|
|
668
|
+
|
|
669
|
+
Extract all scheduled (future-dated) events from ACTION events named "Send Event". These are events dispatched by `ScheduleEvent` rules with a time delay.
|
|
670
|
+
|
|
671
|
+
#### Query
|
|
672
|
+
|
|
673
|
+
```
|
|
674
|
+
event.list({
|
|
675
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
676
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
677
|
+
"count": 200
|
|
678
|
+
})
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
Filter results where `name == "Send Event"`.
|
|
682
|
+
|
|
683
|
+
#### Data Fields
|
|
684
|
+
|
|
685
|
+
| Attribute | Description |
|
|
686
|
+
|-----------|-------------|
|
|
687
|
+
| `Event Name` | Name of the scheduled event (e.g., `ValidationGraceExpired`) |
|
|
688
|
+
| `Event` | Full event object: ID, meta, attributes, entityType, rootEntity info |
|
|
689
|
+
| `Future Dated` | `true` if the event is scheduled for the future, `false` if immediate |
|
|
690
|
+
| `scheduledOn` | Epoch milliseconds when the event will fire (only present when Future Dated is true) |
|
|
691
|
+
|
|
692
|
+
#### Report Format
|
|
693
|
+
|
|
694
|
+
```
|
|
695
|
+
=== Scheduled Event Inventory: ORDER ref=HD-20260222-001 ===
|
|
696
|
+
|
|
697
|
+
# Event Name Entity Type Future Dated Scheduled For Delay
|
|
698
|
+
───────────────────────────────────────────────────────────────────────────────────────────────
|
|
699
|
+
1 ValidateOrder ORDER false (immediate) 0s
|
|
700
|
+
2 ValidationGraceExpired ORDER true 2026-02-22T13:43:30Z 30s
|
|
701
|
+
3 ProcessOrder ORDER false (immediate) 0s
|
|
702
|
+
4 NotifyFCComplete FC false (immediate) 0s
|
|
703
|
+
5 SourceOrder ORDER false (immediate) 0s
|
|
704
|
+
|
|
705
|
+
Future-dated events: 1 of 5
|
|
706
|
+
Scheduled timers: ValidationGraceExpired (30s delay)
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
#### Scheduled Event Analysis
|
|
710
|
+
|
|
711
|
+
For each future-dated event:
|
|
712
|
+
1. **Calculate actual delay** — `scheduledOn` minus the parent event's `generatedOn` timestamp
|
|
713
|
+
2. **Check if it fired** — Search for the matching ORCHESTRATION event after `scheduledOn`
|
|
714
|
+
3. **Check if it was pre-empted** — If a status change occurred before `scheduledOn`, the scheduled event may arrive at a status where no ruleset matches (NO_MATCH is expected)
|
|
715
|
+
4. **Timer health** — Summarize which scheduled events fired vs were pre-empted
|
|
716
|
+
|
|
717
|
+
### 8D: NO_MATCH Deep Diagnostics
|
|
718
|
+
|
|
719
|
+
When a ruleset evaluates but no rule matches (NO_MATCH), the ruleSet audit event contains structured diagnostic data explaining why. This goes beyond the simple "NO_MATCH" detection in Step 5.
|
|
720
|
+
|
|
721
|
+
#### Query
|
|
722
|
+
|
|
723
|
+
```
|
|
724
|
+
event.list({
|
|
725
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
726
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
727
|
+
"count": 200
|
|
728
|
+
})
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
Filter results where `category == "ruleSet"` and attributes contain `message` with "no match" or where `closeMatches` is present.
|
|
732
|
+
|
|
733
|
+
#### Data Fields
|
|
734
|
+
|
|
735
|
+
| Attribute | Description |
|
|
736
|
+
|-----------|-------------|
|
|
737
|
+
| `message` | Summary: "No rules matched for the event and its entity" |
|
|
738
|
+
| `entityStatus` | Entity status when the ruleset was evaluated |
|
|
739
|
+
| `closeMatches` | Array of rulesets that almost matched, with structured mismatch reasons |
|
|
740
|
+
| `closeMatches[].name` | Ruleset name that almost matched |
|
|
741
|
+
| `closeMatches[].mismatchReasons` | Array of specific reasons: subtype mismatch, status mismatch, etc. |
|
|
742
|
+
| `lastRule` | Last rule that was evaluated before NO_MATCH |
|
|
743
|
+
| `lastRuleSet` | Last ruleset that was evaluated |
|
|
744
|
+
|
|
745
|
+
#### Report Format
|
|
746
|
+
|
|
747
|
+
```
|
|
748
|
+
=== NO_MATCH Diagnostics: ORDER ref=HD-20260222-001 ===
|
|
749
|
+
|
|
750
|
+
Event: ValidationGraceExpired → NO_MATCH
|
|
751
|
+
|
|
752
|
+
Entity status at evaluation: ON_VALIDATION
|
|
753
|
+
Close matches:
|
|
754
|
+
1. ProcessOrder
|
|
755
|
+
Mismatch: subtype=HD required, entity subtype=CC
|
|
756
|
+
2. CancelValidation
|
|
757
|
+
Mismatch: status=CANCELLED required, entity status=ON_VALIDATION
|
|
758
|
+
|
|
759
|
+
Diagnosis: ValidationGraceExpired fired after the entity already advanced past ON_VALIDATION
|
|
760
|
+
(pre-empted by ConfirmValidation). This is EXPECTED for timer events.
|
|
761
|
+
|
|
762
|
+
---
|
|
763
|
+
Event: RetryFulfilment → NO_MATCH
|
|
764
|
+
|
|
765
|
+
Entity status at evaluation: SHIPPED
|
|
766
|
+
Close matches:
|
|
767
|
+
1. ProcessFulfilment
|
|
768
|
+
Mismatch: status=CREATED required, entity status=SHIPPED
|
|
769
|
+
|
|
770
|
+
Diagnosis: Retry event arrived too late — fulfilment already shipped.
|
|
771
|
+
Review: is the ScheduleEvent delay too long?
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
#### NO_MATCH Classification
|
|
775
|
+
|
|
776
|
+
| Pattern | Severity | Meaning |
|
|
777
|
+
|---------|----------|---------|
|
|
778
|
+
| Timer event + entity already advanced | INFO | Normal — scheduled event pre-empted by faster progression |
|
|
779
|
+
| Event name has no matching ruleset | HIGH | Misconfigured workflow — event fired but no ruleset exists to handle it |
|
|
780
|
+
| Subtype mismatch in closeMatches | MEDIUM | Event sent to wrong subtype — check SendEvent props |
|
|
781
|
+
| Status mismatch in closeMatches | LOW | Race condition or ordering issue — usually self-resolving |
|
|
782
|
+
|
|
783
|
+
### 8E: Custom Rule Log Extraction
|
|
784
|
+
|
|
785
|
+
Custom rules can emit structured diagnostic logs during execution. These appear as CUSTOM category events with `LogCollection` and `EventAttribute` data.
|
|
786
|
+
|
|
787
|
+
#### Query
|
|
788
|
+
|
|
789
|
+
```
|
|
790
|
+
event.list({
|
|
791
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
792
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
793
|
+
"count": 200
|
|
794
|
+
})
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
Filter results where `category == "CUSTOM"`.
|
|
798
|
+
|
|
799
|
+
#### Data Fields
|
|
800
|
+
|
|
801
|
+
| Attribute | Description |
|
|
802
|
+
|-----------|-------------|
|
|
803
|
+
| `LogCollection` | JSON string containing `logs[]` array: `{ level, source, message, timestamp }` |
|
|
804
|
+
| `EventAttribute` | JSON string containing the triggering event's full attributes |
|
|
805
|
+
|
|
806
|
+
#### Log Structure
|
|
807
|
+
|
|
808
|
+
Each log entry in `LogCollection.logs[]`:
|
|
809
|
+
|
|
810
|
+
```json
|
|
811
|
+
{
|
|
812
|
+
"level": "INFO",
|
|
813
|
+
"source": "ACCT.ext.CreateFulfilmentFromSourcingLocation",
|
|
814
|
+
"message": "Creating fulfilment at location WH-MAIN for order HD-20260222-001",
|
|
815
|
+
"timestamp": "2026-02-22T13:43:05.123Z"
|
|
816
|
+
}
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
#### Report Format
|
|
820
|
+
|
|
821
|
+
```
|
|
822
|
+
=== Custom Rule Logs: ORDER ref=HD-20260222-001 ===
|
|
823
|
+
|
|
824
|
+
Timestamp Level Source Rule Message
|
|
825
|
+
──────────────────────────────────────────────────────────────────────────────────────────────────
|
|
826
|
+
2026-02-22T13:43:05.123Z INFO CreateFulfilmentFromSourcingLocation Creating fulfilment at WH-MAIN
|
|
827
|
+
2026-02-22T13:43:05.456Z INFO CreateFulfilmentFromSourcingLocation Fulfilment ref: FUL-HD-001
|
|
828
|
+
2026-02-22T13:43:06.789Z WARN ValidateInventoryAvailability Low stock: SKU-001 has 2 units
|
|
829
|
+
2026-02-22T13:43:07.012Z INFO SendWebhookWithDynamicAttributes Webhook sent to order.created endpoint
|
|
830
|
+
|
|
831
|
+
Total: 4 log entries (3 INFO, 1 WARN, 0 ERROR)
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
#### Use Cases
|
|
835
|
+
|
|
836
|
+
- **Rule execution visibility**: See exactly what a custom rule decided and why
|
|
837
|
+
- **Debugging silent failures**: When a rule "succeeds" but does something unexpected, logs reveal the internal logic path
|
|
838
|
+
- **Performance profiling**: Log timestamps show how long each rule's internal processing took
|
|
839
|
+
- **Regression detection**: Compare log output between test runs to spot behavioral changes
|
|
840
|
+
|
|
841
|
+
### 8F: Entity Snapshot Extraction
|
|
842
|
+
|
|
843
|
+
Snapshot events capture the full entity state at the moment orchestration begins. These are emitted as `snapshot` category audit events and contain the complete entity (all fields, attributes, items, relationships).
|
|
844
|
+
|
|
845
|
+
#### Query
|
|
846
|
+
|
|
847
|
+
```
|
|
848
|
+
event.list({
|
|
849
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
850
|
+
"context.rootEntityRef": "<ENTITY_REF>",
|
|
851
|
+
"count": 200
|
|
852
|
+
})
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
Filter results where `category == "snapshot"`.
|
|
856
|
+
|
|
857
|
+
#### Data Content by Entity Type
|
|
858
|
+
|
|
859
|
+
**ORDER snapshot includes:**
|
|
860
|
+
- `id`, `ref`, `type`, `subtype`, `status`
|
|
861
|
+
- `customer` — full customer object (firstName, lastName, username)
|
|
862
|
+
- `items[]` — order items with ref, quantity, status, productRef, fulfilmentChoiceRef
|
|
863
|
+
- `fulfilmentChoice` — FC details (ref, deliveryType, deliveryAddress)
|
|
864
|
+
- `attributes[]` — all entity attributes at that moment
|
|
865
|
+
- `createdOn`, `updatedOn`
|
|
866
|
+
|
|
867
|
+
**FULFILMENT snapshot includes:**
|
|
868
|
+
- `id`, `ref`, `type`, `subtype`, `status`
|
|
869
|
+
- `items[]` — fulfilment items with ref, requestedQuantity, filledQuantity
|
|
870
|
+
- `toAddress`, `fromAddress` — shipping addresses
|
|
871
|
+
- `attributes[]` — including trackingNumber, carrierRef, pickedAt, etc.
|
|
872
|
+
- `consignment` — consignment details if created
|
|
873
|
+
- `deliveryType`
|
|
874
|
+
|
|
875
|
+
#### Report Format
|
|
876
|
+
|
|
877
|
+
```
|
|
878
|
+
=== Entity Snapshots: ORDER ref=HD-20260222-001 ===
|
|
879
|
+
|
|
880
|
+
Snapshot #1 — ORDER at CREATED (2026-02-22T13:43:01Z)
|
|
881
|
+
Status: CREATED
|
|
882
|
+
Items: 2 (SKU-001 x1, SKU-002 x2)
|
|
883
|
+
Customer: john.doe
|
|
884
|
+
Delivery: HD to 123 Main St
|
|
885
|
+
Attributes: sourceSystem=E2E_TEST, testRunId=E2E_MULTI_202602221343
|
|
886
|
+
|
|
887
|
+
Snapshot #2 — FULFILMENT at CREATED (2026-02-22T13:43:06Z)
|
|
888
|
+
Status: CREATED
|
|
889
|
+
Items: 2 (SKU-001 x1, SKU-002 x2)
|
|
890
|
+
From: WH-MAIN (Warehouse Main)
|
|
891
|
+
To: 123 Main St
|
|
892
|
+
Delivery Type: HD
|
|
893
|
+
Attributes: (none yet)
|
|
894
|
+
|
|
895
|
+
Snapshot #3 — FULFILMENT at PICKPACK (2026-02-22T13:43:15Z)
|
|
896
|
+
Status: PICKPACK
|
|
897
|
+
Items: 2 (SKU-001 filled:1, SKU-002 filled:2)
|
|
898
|
+
Attributes: pickedAt=2026-02-22T13:43:14Z
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
#### Use Cases
|
|
902
|
+
|
|
903
|
+
- **State verification in E2E tests**: Compare expected entity state at each step against the snapshot — no need to query entity separately
|
|
904
|
+
- **Before/after comparison**: Diff consecutive snapshots to see exactly what changed during a status transition
|
|
905
|
+
- **Attribute tracking**: Verify attributes like `trackingNumber`, `pickedAt`, `deliveredAt` were set at the right time
|
|
906
|
+
- **Customer data audit**: Confirm customer information was correctly attached at order creation
|
|
907
|
+
|
|
908
|
+
#### Integration with `/fluent-e2e-test`
|
|
909
|
+
|
|
910
|
+
After an E2E test completes, snapshots can be used as assertion evidence:
|
|
911
|
+
|
|
912
|
+
```
|
|
913
|
+
For each test step:
|
|
914
|
+
1. Extract snapshot for the entity at the expected status
|
|
915
|
+
2. Verify expected fields are present and correct
|
|
916
|
+
3. Compare with previous snapshot to confirm the transition changed the right fields
|
|
917
|
+
4. Include snapshot diffs in the test report for documentation
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
## Quick Diagnostic Queries
|
|
921
|
+
|
|
922
|
+
### Order lifecycle summary (all events for an order)
|
|
923
|
+
```
|
|
924
|
+
event.list({
|
|
925
|
+
"context.entityRef": "<ORDER_REF>",
|
|
926
|
+
"context.entityType": "ORDER",
|
|
927
|
+
"count": 100
|
|
928
|
+
})
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### All fulfilments for an order
|
|
932
|
+
```graphql
|
|
933
|
+
{
|
|
934
|
+
fulfilments(orderId: [<ORDER_ID>], first: 50) {
|
|
935
|
+
edges {
|
|
936
|
+
node { id ref type status fromLocation { ref } }
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
### All fulfilment choices for an order
|
|
943
|
+
```graphql
|
|
944
|
+
{
|
|
945
|
+
fulfilmentChoices(orderId: [<ORDER_ID>], first: 50) {
|
|
946
|
+
edges {
|
|
947
|
+
node { id ref type status deliveryType }
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
### Check a setting value
|
|
954
|
+
```graphql
|
|
955
|
+
{
|
|
956
|
+
settings(first: 1, context: "RETAILER", contextId: <RETAILER_ID>, name: ["<SETTING_NAME>"]) {
|
|
957
|
+
edges {
|
|
958
|
+
node { id name value lobValue }
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
### Check workflow version deployed
|
|
965
|
+
Use MCP `workflow.list` to verify the expected version is active.
|
|
966
|
+
|
|
967
|
+
## Entity State Stuck — Decision Tree
|
|
968
|
+
|
|
969
|
+
```
|
|
970
|
+
Entity not advancing?
|
|
971
|
+
│
|
|
972
|
+
├─ Check event.list for the entity
|
|
973
|
+
│ ├─ No events found → Event was never sent, or wrong entityRef/entityType
|
|
974
|
+
│ ├─ Events all SUCCESS → State changed but maybe to unexpected status
|
|
975
|
+
│ │ └─ Query entity status → Check workflow state machine
|
|
976
|
+
│ └─ Event FAILED → Inspect event.get for error details
|
|
977
|
+
│ ├─ "Rule exception" → Check rule source code + props
|
|
978
|
+
│ ├─ "No matching ruleset" / NO_MATCH → Event name doesn't match any ruleset; check flexType
|
|
979
|
+
│ ├─ "Entity not found" → Wrong entityRef or entity deleted
|
|
980
|
+
│ └─ "Permission denied" → Check user/retailer scope
|
|
981
|
+
│
|
|
982
|
+
├─ Entity in gate state (waiting for children)?
|
|
983
|
+
│ └─ Query all child entities
|
|
984
|
+
│ ├─ All children in terminal state → Gate ruleset bug (check statuses prop)
|
|
985
|
+
│ └─ Some children not terminal → Wait for children to complete
|
|
986
|
+
│
|
|
987
|
+
└─ Event sent but still PENDING?
|
|
988
|
+
└─ Async queue delay — wait 15-30s and re-check
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
## Manual Intervention Options
|
|
992
|
+
|
|
993
|
+
After identifying the root cause, choose the appropriate intervention:
|
|
994
|
+
|
|
995
|
+
### Option 1: Re-send the Event
|
|
996
|
+
|
|
997
|
+
If the root cause is fixed (e.g., workflow deployed, setting created, data corrected), re-send the original event:
|
|
998
|
+
|
|
999
|
+
```
|
|
1000
|
+
event.send({
|
|
1001
|
+
"name": "<EVENT_NAME>",
|
|
1002
|
+
"entityType": "<ENTITY_TYPE>",
|
|
1003
|
+
"entityRef": "<ENTITY_REF>",
|
|
1004
|
+
"retailerId": "<RETAILER_ID>"
|
|
1005
|
+
})
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
### Option 2: Run-Once Workflow (Targeted Rule Execution)
|
|
1009
|
+
|
|
1010
|
+
Execute specific rules without triggering the full workflow. Useful for re-triggering a failed webhook, running a data correction rule, or executing a rule that isn't in the current deployed workflow.
|
|
1011
|
+
|
|
1012
|
+
Send to `POST /api/v4.1/event/sync` with the workflow attached in `meta`:
|
|
1013
|
+
|
|
1014
|
+
```json
|
|
1015
|
+
{
|
|
1016
|
+
"name": "FixEvent",
|
|
1017
|
+
"retailerId": "1",
|
|
1018
|
+
"entityType": "FULFILMENT",
|
|
1019
|
+
"entityId": "143",
|
|
1020
|
+
"meta": {
|
|
1021
|
+
"workflow": {
|
|
1022
|
+
"retailerId": "1",
|
|
1023
|
+
"version": "1.0",
|
|
1024
|
+
"rulesets": [{
|
|
1025
|
+
"name": "FixEvent",
|
|
1026
|
+
"type": "FULFILMENT",
|
|
1027
|
+
"eventType": "NORMAL",
|
|
1028
|
+
"rules": [{ "name": "ACCOUNT.module.RuleName", "props": { "key": "value" } }],
|
|
1029
|
+
"triggers": [],
|
|
1030
|
+
"userActions": []
|
|
1031
|
+
}]
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Key:** The event `name` must match the ruleset `name`. Any rule in the account can be used — it doesn't need to exist in the deployed workflow. Webhooks and mutations execute; inline events are ignored.
|
|
1038
|
+
|
|
1039
|
+
For full run-once workflow reference → `/fluent-event-api`.
|
|
1040
|
+
|
|
1041
|
+
### Option 3: Direct GraphQL Mutation
|
|
1042
|
+
|
|
1043
|
+
For data corrections that don't require workflow logic, use `graphql.query` with a mutation directly.
|
|
1044
|
+
|
|
1045
|
+
## Causal Chain Playbook
|
|
1046
|
+
|
|
1047
|
+
Compact playbook for tracing event causality when diagnosing failures.
|
|
1048
|
+
|
|
1049
|
+
### Principle: One ORCHESTRATION Event = One Execution Journey
|
|
1050
|
+
|
|
1051
|
+
Every ORCHESTRATION event triggers a chain of ORCHESTRATION_AUDIT events. The audit events contain the detailed execution (rulesets, rules, actions) linked via `sourceEvents`.
|
|
1052
|
+
|
|
1053
|
+
### Step-by-Step Chain Reconstruction
|
|
1054
|
+
|
|
1055
|
+
1. **Find the ORCHESTRATION event** that triggered execution:
|
|
1056
|
+
```
|
|
1057
|
+
event.list({ "context.entityRef": "<REF>", "eventType": "ORCHESTRATION", "count": 50 })
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
2. **Find all audit events it spawned** (linked by sourceEvents):
|
|
1061
|
+
```
|
|
1062
|
+
event.list({
|
|
1063
|
+
"eventType": "ORCHESTRATION_AUDIT",
|
|
1064
|
+
"context.entityRef": "<REF>",
|
|
1065
|
+
"from": "<orchestration_event_timestamp_minus_1s>",
|
|
1066
|
+
"to": "<orchestration_event_timestamp_plus_60s>",
|
|
1067
|
+
"count": 200
|
|
1068
|
+
})
|
|
1069
|
+
```
|
|
1070
|
+
Match: audit events whose `context.sourceEvents` array contains the ORCHESTRATION event ID.
|
|
1071
|
+
|
|
1072
|
+
3. **Classify audit events by category:**
|
|
1073
|
+
- `snapshot` — entity state before execution
|
|
1074
|
+
- `ruleSet` — ruleset-level timing (`startTimer`/`stopTimer`)
|
|
1075
|
+
- `rule` — individual rule execution
|
|
1076
|
+
- `ACTION` — actions performed (SetState, SendEvent, Send Webhook)
|
|
1077
|
+
- `exception` — error with stack trace
|
|
1078
|
+
- `CUSTOM` — custom log output from rules
|
|
1079
|
+
|
|
1080
|
+
4. **Trace the failure point:**
|
|
1081
|
+
- Find first `exception` category event → check `attributes.message` and `attributes.exception.stackTrace`
|
|
1082
|
+
- Check the `ruleSet` event → its `name` is the ruleset that was executing
|
|
1083
|
+
- Check the `ACTION` events → see what actions completed before the failure
|
|
1084
|
+
|
|
1085
|
+
5. **If sourceEvents is sparse:** fall back to entity context + time window correlation (see `/fluent-event-api` → Causality Correlation Workflow)
|
|
1086
|
+
|
|
1087
|
+
### Webhook Failure Triage
|
|
1088
|
+
|
|
1089
|
+
When a webhook fails during event processing:
|
|
1090
|
+
|
|
1091
|
+
1. **Find the webhook ACTION event:**
|
|
1092
|
+
```
|
|
1093
|
+
event.list({
|
|
1094
|
+
"category": "ACTION",
|
|
1095
|
+
"name": "Send Webhook",
|
|
1096
|
+
"context.rootEntityRef": "<REF>",
|
|
1097
|
+
"count": 50
|
|
1098
|
+
})
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
2. **Inspect webhook attributes:**
|
|
1102
|
+
- `Request Endpoint` — the target URL
|
|
1103
|
+
- `Response code` — HTTP status (4xx = client error, 5xx = server error, 0 = connection timeout)
|
|
1104
|
+
- `Response Body` — error details from the remote server
|
|
1105
|
+
- `Response Headers` — may reveal rate limiting or auth issues
|
|
1106
|
+
|
|
1107
|
+
3. **Check the webhook setting:**
|
|
1108
|
+
```graphql
|
|
1109
|
+
{
|
|
1110
|
+
settings(first: 1, context: "RETAILER", contextId: <ID>, name: ["<webhook-setting-name>"]) {
|
|
1111
|
+
edges { node { id name value lobValue } }
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
4. **Common webhook failures:**
|
|
1117
|
+
|
|
1118
|
+
| Response Code | Likely Cause | Action |
|
|
1119
|
+
|--------------|--------------|--------|
|
|
1120
|
+
| 0 | Connection timeout / DNS failure | Check endpoint URL and network |
|
|
1121
|
+
| 401/403 | Auth credentials invalid or expired | Update webhook setting credentials |
|
|
1122
|
+
| 404 | Endpoint URL changed | Update webhook setting URL |
|
|
1123
|
+
| 429 | Rate limited | Implement retry/backoff or reduce event frequency |
|
|
1124
|
+
| 500+ | Remote server error | Check target system logs |
|
|
1125
|
+
|
|
1126
|
+
## Integration with Other Skills
|
|
1127
|
+
|
|
1128
|
+
| Task | Skill |
|
|
1129
|
+
|------|-------|
|
|
1130
|
+
| Event model, filter parameters, causality chains | `/fluent-event-api` |
|
|
1131
|
+
| MCP payload syntax and tool limits | `/fluent-mcp-tools` |
|
|
1132
|
+
| Rule/source correlation | `/fluent-custom-code` |
|
|
1133
|
+
| Workflow topology and trigger intent | `/fluent-workflow-analyzer` |
|
|
1134
|
+
|
|
1135
|
+
## Reporting
|
|
1136
|
+
|
|
1137
|
+
After diagnosis, report:
|
|
1138
|
+
|
|
1139
|
+
1. **Entity:** ref, type, current status, expected status
|
|
1140
|
+
2. **Failing event:** name, ID, status, error message
|
|
1141
|
+
3. **Root cause:** which ruleset/rule failed and why
|
|
1142
|
+
4. **Fix:** what needs to change (code, workflow, setting, data)
|
|
1143
|
+
5. **Recommendation:** specific file/line/config to modify
|