@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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +622 -0
  3. package/bin/cli.mjs +1973 -0
  4. package/content/cli/agents/fluent-cli/agent.json +149 -0
  5. package/content/cli/agents/fluent-cli.md +132 -0
  6. package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
  7. package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
  8. package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
  9. package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
  10. package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
  11. package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
  12. package/content/cli/skills/fluent-connect/SKILL.md +886 -0
  13. package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
  14. package/content/cli/skills/fluent-profile/SKILL.md +180 -0
  15. package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
  16. package/content/dev/agents/fluent-dev/agent.json +88 -0
  17. package/content/dev/agents/fluent-dev.md +525 -0
  18. package/content/dev/reference-modules/catalog.json +4754 -0
  19. package/content/dev/skills/fluent-build/SKILL.md +192 -0
  20. package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
  21. package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
  22. package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
  23. package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
  24. package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
  25. package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
  26. package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
  27. package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
  28. package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
  29. package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
  30. package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
  31. package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
  32. package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
  33. package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
  34. package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
  35. package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
  36. package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
  37. package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
  38. package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
  39. package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
  40. package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
  41. package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
  42. package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
  43. package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
  44. package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
  45. package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
  46. package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
  47. package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
  48. package/content/mcp-extn/agents/fluent-mcp.md +69 -0
  49. package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
  50. package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
  51. package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
  52. package/content/rfl/agents/fluent-rfl.md +56 -0
  53. package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
  54. package/docs/CAPABILITY_MAP.md +77 -0
  55. package/docs/CLI_COVERAGE.md +47 -0
  56. package/docs/DEV_WORKFLOW.md +802 -0
  57. package/docs/FLOW_RUN.md +142 -0
  58. package/docs/USE_CASES.md +404 -0
  59. package/metadata.json +156 -0
  60. package/package.json +51 -0
@@ -0,0 +1,945 @@
1
+ ---
2
+ name: fluent-event-api
3
+ description: Understand and work with the Fluent Commerce Event API. Covers event types, categories, statuses, routing, context model, filtering, execution model, run-once workflows, log actions, and operational query patterns for debugging workflows dynamically, including mutation/webhook/scheduled payload forensics from audit actions. Triggers on "event api", "event types", "event categories", "query events", "event model", "event status", "analyze events", "event history", "bulk events", "run once", "log action".
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6
+ argument-hint: [--entity-ref <ref>] [--entity-type ORDER|FULFILMENT] [--status FAILED|SUCCESS|SCHEDULED] [--type ORCHESTRATION|ORCHESTRATION_AUDIT]
7
+ ---
8
+
9
+ # Fluent Commerce Event API
10
+
11
+ Comprehensive reference for understanding and querying the Fluent Commerce Event API. Covers the event data model, how the orchestration engine processes events, all filter parameters, execution threading, run-once workflows, log actions, and operational query patterns for debugging workflows dynamically.
12
+
13
+ ## Ownership Boundary
14
+
15
+ This skill owns event data model knowledge, query patterns, execution model, and operational analysis.
16
+
17
+ - **Sending events** (build/send/dryRun flows) → `/fluent-mcp-tools`
18
+ - **One-call runtime forensics** (mutations/webhooks/scheduled/exceptions) → `event.flowInspect` via `/fluent-mcp-tools`
19
+ - **Debugging a specific stuck entity** → `/fluent-trace`
20
+ - **Testing event sequences** → `/fluent-e2e-test`
21
+ - **Discovering available actions at a status** → `/fluent-transition-api`
22
+
23
+ ## REST API Reference
24
+
25
+ ### Endpoints
26
+
27
+ | Method | Path | Description | Mode |
28
+ |--------|------|-------------|------|
29
+ | GET | `/api/v4.1/event/{eventId}` | Get a single event by ID | — |
30
+ | GET | `/api/v4.1/event` | List/filter events with query parameters | — |
31
+ | POST | `/api/v4.1/event/async` | Create event asynchronously | Async (recommended for integrations) |
32
+ | POST | `/api/v4.1/event/sync` | Create event synchronously | Sync (UI user actions only) |
33
+
34
+ **Base URL:** `https://<accountId>.<env>.api.fluentretail.com/api/v4.1/event`
35
+ **Auth:** OAuth required. **Scheme:** HTTPS only. **Content-Type:** `application/json`
36
+
37
+ ### API Limits and Defaults
38
+
39
+ | Constraint | Value |
40
+ |------------|-------|
41
+ | Request size limit | 10 MB (returns HTTP 413 if exceeded) |
42
+ | Max time range for GET /event | Typically **4 months back + 1 month forward** from current time (API returns error if exceeded; treat validation errors as runtime truth for your tenant) |
43
+ | Recommended time range | 5 minutes (due to high event volume) |
44
+ | Default `from` | Usually today minus ~30 days (tenant-dependent) |
45
+ | Default `to` | Now |
46
+ | Default `count` | 100 |
47
+ | Max `count` (REST API) | 5,000 (may timeout with large result sets) |
48
+
49
+ **MCP vs REST count limits:** The MCP `event.list` tool caps at 500 per request. The REST API supports up to 5,000 but may timeout with large result sets. For aggregate analytics across many events, use `metrics.topEvents` (client-side aggregation across `event.list` pages) or `event.list` with narrow time windows and targeted filters (`eventStatus`, `category`, `eventType`). Use `metrics.query` for PromQL-based Prometheus queries when available.
50
+
51
+ **Window guardrail:** Treat Event API validation errors as runtime truth for your tenant. If a query window is rejected, shrink the interval and paginate (do not rely on hardcoded global limits).
52
+
53
+ **Scheduled events:** Once created, scheduled events cannot be cancelled or modified. Monitor via `event.list` with `eventStatus: "SCHEDULED"`. The event will process when its scheduled time arrives.
54
+
55
+ ### POST Request Body (async and sync)
56
+
57
+ ```json
58
+ {
59
+ "name": "EventName",
60
+ "retailerId": "1",
61
+ "entityType": "ORDER",
62
+ "entityId": "1234",
63
+ "entityRef": "ON-1234",
64
+ "rootEntityType": "ORDER",
65
+ "rootEntityId": "1234",
66
+ "rootEntityRef": "ON-1234",
67
+ "attributes": { "key": "value" }
68
+ }
69
+ ```
70
+
71
+ **Required fields:** `name`, `entityType`, `rootEntityType`, plus at least one of `entityId`/`entityRef` and one of `rootEntityId`/`rootEntityRef`.
72
+ **retailerId:** Required when authenticated as an Account User (account-level users may access multiple retailers).
73
+
74
+ ### entityId vs entityRef — Legacy and New Domains
75
+
76
+ | Identifier | Use For | Examples |
77
+ |------------|---------|---------|
78
+ | `entityId` | Legacy entities | ORDER, FULFILMENT, LOCATION, FULFILMENT_OPTIONS |
79
+ | `entityRef` | Newer entities | VIRTUAL_POSITION, VIRTUAL_CATALOGUE, INVENTORY_CATALOGUE, INVENTORY_POSITION, BILLING_ACCOUNT, RETURN_ORDER |
80
+
81
+ Both fields can be supplied together. For legacy entities, `entityId` is the primary lookup. For newer entities (global inventory, billing, returns), `entityRef` is the primary lookup.
82
+
83
+ ### Pre-Send Validation Checklist (Event Contract Guard)
84
+
85
+ Before sending an event (`event.send` or raw POST), validate:
86
+
87
+ 1. `name` matches an intended ruleset name (or a known status-trigger flow).
88
+ 2. `entityType` and `rootEntityType` are present and accurate.
89
+ 3. At least one identifier exists for each level:
90
+ - entity: `entityId` or `entityRef`
91
+ - root entity: `rootEntityId` or `rootEntityRef`
92
+ 4. `retailerId` is set when acting as an account-level user.
93
+ 5. Required event attributes are present:
94
+ - check `workflow.transitions` (`userActions[].attributes`) and/or rule contracts from `plugin.list`.
95
+ 6. Time-window follow-up is prepared for async sends:
96
+ - query with `event.list` using entity/root context + bounded `from/to` window.
97
+
98
+ ### Sync Response
99
+
100
+ The sync endpoint (`/event/sync`) returns a richer response than async:
101
+
102
+ ```json
103
+ {
104
+ "eventId": "uuid",
105
+ "entityId": "994",
106
+ "eventStatus": "COMPLETE"
107
+ }
108
+ ```
109
+
110
+ Possible `eventStatus` values in sync response: `COMPLETE`, `FAILED`, `NO_MATCH`.
111
+
112
+ `NO_MATCH` means no ruleset matched the incoming event — the event name didn't match any ruleset name, and no status-trigger ruleset matched either. This usually indicates a workflow configuration issue.
113
+
114
+ ### Async Response
115
+
116
+ The async endpoint (`/event/async`) returns only the event ID:
117
+
118
+ ```json
119
+ { "id": "uuid" }
120
+ ```
121
+
122
+ Poll `event.list` with entity context and a time window to check processing result.
123
+
124
+ ## Event Data Model
125
+
126
+ ### Event Structure
127
+
128
+ Every event in Fluent Commerce has this shape:
129
+
130
+ ```json
131
+ {
132
+ "id": "uuid",
133
+ "name": "EventName",
134
+ "type": "ORCHESTRATION | ORCHESTRATION_AUDIT | API | GENERAL | ...",
135
+ "accountId": "ACCOUNT_NAME",
136
+ "retailerId": "2",
137
+ "category": "ORDER WORKFLOW | ACTION | ruleSet | exception | ...",
138
+ "context": {
139
+ "sourceEvents": ["parent-event-uuid"],
140
+ "entityType": "ORDER | FULFILMENT | VIRTUAL_CATALOGUE | ...",
141
+ "entityId": "6611",
142
+ "entityRef": "ORDER_REF_001",
143
+ "rootEntityType": "ORDER",
144
+ "rootEntityId": "6611",
145
+ "rootEntityRef": "ORDER_REF_001"
146
+ },
147
+ "eventStatus": "SUCCESS | FAILED | COMPLETE | SCHEDULED | PENDING | NO_MATCH",
148
+ "attributes": [
149
+ { "name": "key", "value": "val", "type": "STRING | INTEGER | BOOLEAN | OBJECT" }
150
+ ],
151
+ "source": "Fluent-API | <hashcode>.<RulesetName> | plugin",
152
+ "generatedBy": "username | Rubix User | ACCOUNT-RETAILER-rubix",
153
+ "generatedOn": "ISO-8601 timestamp",
154
+ "recordedBy": "username | Rubix User",
155
+ "recordedOn": "ISO-8601 timestamp",
156
+ "meta": null
157
+ }
158
+ ```
159
+
160
+ ### Event Types
161
+
162
+ | Type | What It Represents | Who Generates It |
163
+ |------|-------------------|-----------------|
164
+ | `ORCHESTRATION` | A workflow trigger event — an instruction to the orchestration engine to process rules | External API calls, `SendEvent` rules, entity creation (`CREATE` event), scheduled events |
165
+ | `ORCHESTRATION_AUDIT` | An audit trail record of what happened during rule execution. One `ORCHESTRATION` event produces multiple `ORCHESTRATION_AUDIT` children | The orchestration engine (Rubix) internally |
166
+ | `API` | A REST API call record (legacy audit) | The Fluent API layer when REST endpoints are called |
167
+ | `GENERAL` | General system activity record (non-triggering) | Various internal platform components |
168
+ | `INTEGRATION` | Integration event record | Integration subsystems |
169
+ | `SECURITY` | Security-related event record | Authentication and authorization subsystems |
170
+
171
+ **Key insight:** `ORCHESTRATION` and `ORCHESTRATION_AUDIT` are the primary types for workflow analysis. `API`, `GENERAL`, `INTEGRATION`, and `SECURITY` are informational — they describe what happened but do not trigger workflow execution.
172
+
173
+ ### Event Categories
174
+
175
+ | Category | Meaning | Type |
176
+ |----------|---------|------|
177
+ | `ORDER WORKFLOW` | Workflow orchestration trigger event (the inbound event) | `ORCHESTRATION` |
178
+ | `ACTION` | A rule action executed during ruleset processing (SetState, SendEvent, Send Webhook) | `ORCHESTRATION_AUDIT` |
179
+ | `ruleSet` | A ruleset-level execution record (start/stop timing) | `ORCHESTRATION_AUDIT` |
180
+ | `rule` | An individual rule execution record | `ORCHESTRATION_AUDIT` |
181
+ | `exception` | A rule or engine exception during processing | `ORCHESTRATION_AUDIT` |
182
+ | `snapshot` | An entity snapshot taken during execution | `ORCHESTRATION_AUDIT` |
183
+ | `CUSTOM` | A log action event emitted by custom rule code via `context.action().log()` | `ORCHESTRATION_AUDIT` |
184
+ | `BATCH` | Batch processing events | `ORCHESTRATION_AUDIT` |
185
+
186
+ ### Event Statuses
187
+
188
+ | Status | Meaning | Applies To |
189
+ |--------|---------|-----------|
190
+ | `SUCCESS` | Event processed or action completed successfully | Both types |
191
+ | `FAILED` | Event processing or action failed | Both types |
192
+ | `COMPLETE` | Workflow event fully processed (all rules executed) | `ORCHESTRATION` |
193
+ | `SCHEDULED` | Future-dated event, not yet processed | `ORCHESTRATION` |
194
+ | `PENDING` | Event queued, waiting for processing | `ORCHESTRATION` |
195
+ | `NO_MATCH` | No ruleset matched the incoming event | `ORCHESTRATION` |
196
+
197
+ **Status transitions:**
198
+ ```
199
+ PENDING → (engine picks up) → SUCCESS / FAILED / COMPLETE / NO_MATCH
200
+ SCHEDULED → (scheduled time arrives) → PENDING → SUCCESS / FAILED / COMPLETE / NO_MATCH
201
+ ```
202
+
203
+ ### Constants Mini-Reference (Canonical Values)
204
+
205
+ Use these values when filtering and when writing diagnostics:
206
+
207
+ | Constant group | Canonical values |
208
+ |---|---|
209
+ | `eventType` | `ORCHESTRATION`, `ORCHESTRATION_AUDIT`, `API`, `GENERAL`, `INTEGRATION`, `SECURITY` |
210
+ | `eventStatus` | `SUCCESS`, `FAILED`, `COMPLETE`, `SCHEDULED`, `PENDING`, `NO_MATCH` |
211
+ | `category` (common) | `ORDER WORKFLOW`, `ACTION`, `ruleSet`, `rule`, `exception`, `snapshot`, `CUSTOM`, `BATCH` |
212
+
213
+ Treat additional values as environment-dependent extensions; do not hard-fail on unknown values.
214
+
215
+ ### Enum Normalization (Cross-Doc Variants)
216
+
217
+ Some tenant docs and analytics layers use alias values. Normalize them before reasoning:
218
+
219
+ | Semantic meaning | Canonical Event API value | Alias values seen in docs/tools |
220
+ |---|---|---|
221
+ | Inbound orchestration category | `ORDER WORKFLOW` | `ORDER_WORKFLOW` |
222
+ | Failed terminal state | `FAILED` | `ERROR` |
223
+ | In-flight / not-terminal | `PENDING` (and `SCHEDULED`) | `PROCESSING` |
224
+ | Successful terminal state | `COMPLETE` / `SUCCESS` | `SUCCESS` |
225
+
226
+ Use canonical Event API values in `event.list` filters. Keep alias mapping only for interpretation/reporting.
227
+
228
+ ### Orchestratable Entity Types
229
+
230
+ **Root Entity Types** (top-level entities that own workflows):
231
+
232
+ `ORDER`, `LOCATION`, `FULFILMENT_OPTIONS`, `PRODUCT_CATALOGUE`, `INVENTORY_CATALOGUE`, `VIRTUAL_CATALOGUE`, `CONTROL_GROUP`, `RETURN_ORDER`, `BILLING_ACCOUNT`, `JOB`
233
+
234
+ **All Entity Types** (includes child/sub-entities):
235
+
236
+ `ORDER`, `FULFILMENT`, `FULFILMENT_CHOICE`, `ARTICLE`, `CONSIGNMENT`, `CARRIER_CONSIGNMENT`, `LOCATION`, `WAVE`, `FULFILMENT_OPTIONS`, `FULFILMENT_PLAN`, `PRODUCT_CATALOGUE`, `CATEGORY`, `PRODUCT`, `INVENTORY_CATALOGUE`, `INVENTORY_POSITION`, `INVENTORY_QUANTITY`, `VIRTUAL_CATALOGUE`, `VIRTUAL_POSITION`, `CONTROL_GROUP`, `CONTROL`, `RETURN_ORDER`, `RETURN_FULFILMENT`, `BILLING_ACCOUNT`, `CREDIT_MEMO`, `FINANCIAL_TRANSACTION`, `MANIFEST`, `BATCH`
237
+
238
+ **Additional traceable entity types** (appear in event context for tracing but typically don't own workflows):
239
+
240
+ `NETWORK`, `STORE_ADDRESS`
241
+
242
+ ### Entity Hierarchy — Root to Child Mapping
243
+
244
+ When sending events, `rootEntityType` must be the top-level entity that owns the workflow. `entityType` targets the specific entity within that hierarchy. Use this table to determine the correct `rootEntityType` for any `entityType`:
245
+
246
+ | Root Entity Type | Allowed Child Entity Types | Workflow Pattern |
247
+ |---|---|---|
248
+ | `ORDER` | `FULFILMENT`, `ARTICLE`, `CONSIGNMENT`, `CARRIER_CONSIGNMENT`, `MANIFEST`, `FINANCIAL_TRANSACTION`, `FULFILMENT_OPTIONS`, `FULFILMENT_PLAN` | `ORDER::<subtype>` (e.g., `ORDER::HD`, `ORDER::CC`) |
249
+ | `LOCATION` | `WAVE` | `LOCATION::<subtype>` |
250
+ | `FULFILMENT_OPTIONS` | `FULFILMENT_PLAN` | `FULFILMENT_OPTIONS::<subtype>` |
251
+ | `PRODUCT_CATALOGUE` | `CATEGORY`, `PRODUCT` | `PRODUCT_CATALOGUE::<subtype>` |
252
+ | `INVENTORY_CATALOGUE` | `INVENTORY_POSITION`, `INVENTORY_QUANTITY` | `INVENTORY_CATALOGUE::<subtype>` |
253
+ | `VIRTUAL_CATALOGUE` | `VIRTUAL_POSITION` | `VIRTUAL_CATALOGUE::<subtype>` |
254
+ | `CONTROL_GROUP` | `CONTROL` | `CONTROL_GROUP::<subtype>` |
255
+ | `RETURN_ORDER` | `RETURN_FULFILMENT` | `RETURN_ORDER::<subtype>` |
256
+ | `BILLING_ACCOUNT` | `CREDIT_MEMO` | `BILLING_ACCOUNT::<subtype>` |
257
+ | `JOB` | `BATCH` | `JOB::<subtype>` |
258
+
259
+ **Self-referencing:** When the event targets the root entity itself, `entityType` = `rootEntityType` (e.g., an ORDER event has both set to `ORDER`).
260
+
261
+ **FULFILMENT_OPTIONS note:** This entity type appears both as a root entity (owns its own workflow) and as a child of ORDER in some contexts. When targeting FULFILMENT_OPTIONS directly, use it as `rootEntityType`. When tracing from an order perspective, the order's FULFILMENT_OPTIONS events will have `rootEntityType=ORDER`.
262
+
263
+ ### Event Context — Entity vs Root Entity
264
+
265
+ Every event has a **target entity** and an optional **root entity**:
266
+
267
+ | Field | Purpose | Example |
268
+ |-------|---------|---------|
269
+ | `entityType` | The entity this event targets | `FULFILMENT` |
270
+ | `entityId` | Numeric ID of the target entity | `10269` |
271
+ | `entityRef` | Reference of the target entity | `107ae9df-9441-...` |
272
+ | `rootEntityType` | The top-level parent entity | `ORDER` |
273
+ | `rootEntityId` | Numeric ID of the root entity | `6611` |
274
+ | `rootEntityRef` | Reference of the root entity | `E2E_HD_20260222_RUN1` |
275
+
276
+ **Why this matters:** A fulfilment event has `entityType=FULFILMENT` but `rootEntityType=ORDER`. This lets you trace all events across an entire order lifecycle — including its child fulfilments — by filtering on `rootEntityRef`.
277
+
278
+ ### Source Events — Tracing Causality
279
+
280
+ The `context.sourceEvents` array contains the parent event IDs that caused this event:
281
+
282
+ - `ORCHESTRATION` events from `event.send` have `sourceEvents: []` (they are root causes)
283
+ - `ORCHESTRATION_AUDIT` events have `sourceEvents: ["<parent-orchestration-event-id>"]`
284
+ - `ORCHESTRATION` events created by `SendEvent` rules have `sourceEvents: ["<audit-event-that-sent-them>"]` — but only sometimes; Fluent doesn't always populate this
285
+
286
+ **Caveat:** `sourceEvents` can be missing/incomplete in some environments. For causality, combine it with time windows and entity/root-entity context.
287
+
288
+ ### Causality Fallback Playbook (When `sourceEvents` Is Sparse)
289
+
290
+ When direct parent pointers are absent:
291
+
292
+ 1. Fetch anchor event with `event.get(eventId)`.
293
+ 2. Build a tight correlation window around `generatedOn` (for example `-1s` to `+30s`).
294
+ 3. Query `event.list` constrained by:
295
+ - `context.entityRef` / `context.entityType`
296
+ - `context.rootEntityRef` / `context.rootEntityType` (for cross-entity flows)
297
+ - `eventType: ORCHESTRATION_AUDIT`
298
+ 4. Order by timestamp and group by `category` (`ruleSet`, `rule`, `ACTION`, `exception`).
299
+ 5. Reconstruct chain by sequence:
300
+ - trigger `ORCHESTRATION` -> related audit trail -> next emitted `ORCHESTRATION`.
301
+ 6. Validate inferred edges with rule/action evidence (`attributes.Event Name`, webhook attributes, exception fields).
302
+
303
+ ### The `source` Field
304
+
305
+ | Value Pattern | Meaning |
306
+ |---------------|---------|
307
+ | `Fluent-API` | Event sent via REST API (`event.send`) |
308
+ | `<hashcode>.<RulesetName>` | Generated by the orchestration engine during ruleset execution |
309
+ | `plugin` | Generated by a custom rule (typically log action events) |
310
+ | `null` | Exception or system-generated event |
311
+ | `module_test_admin` (or username) | API event tied to a specific user |
312
+
313
+ ### Event Attributes
314
+
315
+ Attributes carry data with events. Different event categories have different attribute patterns:
316
+
317
+ **ACTION events (rule audit):**
318
+ - `Event Name` — the event name a SendEvent rule dispatched
319
+ - `Event` — full nested event object (for SendEvent audit trail)
320
+ - `Future Dated` — boolean, whether the event is scheduled for the future
321
+ - `Request Endpoint`, `Response Body`, `Response code`, `Response Headers` — for webhook audit
322
+ - `startTimer`, `stopTimer` — execution timing in epoch milliseconds
323
+
324
+ **Exception events:**
325
+ - `exception` — full Java exception object with stackTrace, message, code
326
+ - `lastRule` — the rule that was executing when the exception occurred (can be null)
327
+ - `lastRuleSet` — the ruleset being executed
328
+ - `message` — human-readable error message
329
+
330
+ **Log Action events (category=CUSTOM):**
331
+ - `action` — the action label
332
+ - `message` — the log message string
333
+ - `detailedMessage` — the detailed log message string
334
+
335
+ ## Event Execution Model
336
+
337
+ ### Execution Context
338
+
339
+ When an `ORCHESTRATION` event is received by the Rubix orchestration engine:
340
+
341
+ 1. **Initiate Execution Context** — includes the event and the entity specified by the event
342
+ 2. **Load workflow** — locate the appropriate workflow based on entity type, subtype, and version
343
+ 3. **Match rulesets** — find rulesets matching the event signature (see Ruleset Matching below)
344
+ 4. **Execute sequentially** — all matching rulesets are executed one by one in sequence
345
+
346
+ ### Ruleset Matching
347
+
348
+ The engine matches events to rulesets using four criteria:
349
+
350
+ 1. **Event Name = Ruleset Name** (exact match, case-sensitive) — primary match
351
+ 2. **Entity Type** — e.g., `ORDER`
352
+ 3. **Entity Subtype** — e.g., `HD`
353
+ 4. **Entity Status** — e.g., `BOOKED` (via `triggers: [{ "status": "BOOKED" }]`)
354
+
355
+ **Priority:** If both a name-match and a status-trigger match exist, the name-match takes priority. If no ruleset matches, the event gets `NO_MATCH` status.
356
+
357
+ ### Threading Model
358
+
359
+ - **Single-threaded execution:** Each event that triggers execution is processed in its own single thread. The workflow execution is linear.
360
+ - **No parallel split:** There is no concurrent processing within the same execution context.
361
+ - **Flow control events (same thread):** If a rule produces an event intended to trigger another ruleset **within the same workflow/root entity**, it is matched and queued for execution within the same execution thread.
362
+ - **Cross-workflow events (different thread):** Events produced for a **different workflow or different root entity** are NOT executed in the same context — they are processed separately by Rubix in a different thread.
363
+
364
+ ### Internal Queues
365
+
366
+ The engine uses two internal queues:
367
+
368
+ - **Internal Event Queue** — holds events waiting for ruleset matching and execution
369
+ - **Internal Action Queue** — holds actions (webhooks, scheduled events, mutations) produced by rules; these are processed separately after rule execution
370
+
371
+ ## How Events Route to Rulesets
372
+
373
+ ```
374
+ 1. Receive ORCHESTRATION event (name, entityType, entityStatus, entitySubtype)
375
+ 2. Find workflow for flexType = entityType::entitySubtype (e.g., ORDER::HD)
376
+ 3. Match ruleset by:
377
+ a. Ruleset name == event name (exact match, case-sensitive)
378
+ b. OR ruleset has triggers: [{ "status": "<entityStatus>" }] (status trigger)
379
+ 4. Execute all rules in the matched ruleset sequentially
380
+ 5. For each rule action, write ORCHESTRATION_AUDIT event
381
+ 6. If exception, write ORCHESTRATION_AUDIT with category "exception"
382
+ 7. If no match, write event with status NO_MATCH
383
+ ```
384
+
385
+ ## Log Actions (Custom Audit Events)
386
+
387
+ Custom rules can emit log events for monitoring and audit using:
388
+
389
+ ```java
390
+ context.action().log(message, detailedMessage, attributes);
391
+ ```
392
+
393
+ These produce `ORCHESTRATION_AUDIT` events with `category=CUSTOM` and `name=CREATE_Log`, `source=plugin`.
394
+
395
+ **Query log action events:**
396
+ ```
397
+ event.list({
398
+ "category": "CUSTOM",
399
+ "from": "2026-02-20T00:00:00Z",
400
+ "to": "2026-02-20T23:59:59Z",
401
+ "count": 200
402
+ })
403
+ ```
404
+
405
+ Log action events are useful for business monitoring, audit trails, and debugging custom rule behavior. The `attributes` array contains the `message`, `detailedMessage`, and any additional key-value pairs passed by the rule.
406
+
407
+ ## Manual Event with Run-Once Workflow
408
+
409
+ The Fluent platform supports sending events with a workflow "attached" in the `meta` section. Rubix executes only the rules defined in the attached workflow — this is primarily used for operational recovery and manual intervention.
410
+
411
+ ### When to Use
412
+
413
+ - Re-trigger a failed webhook without re-running the entire ruleset
414
+ - Execute a specific rule on an entity for data correction
415
+ - Run a subset of rules without modifying the deployed workflow
416
+ - One-off operational tasks
417
+
418
+ ### Structure
419
+
420
+ ```json
421
+ {
422
+ "name": "EventName",
423
+ "accountId": "ACCOUNT_ID",
424
+ "retailerId": "1",
425
+ "entityType": "FULFILMENT",
426
+ "entityId": "143",
427
+ "meta": {
428
+ "workflow": {
429
+ "retailerId": "1",
430
+ "version": "1.0",
431
+ "rulesets": [
432
+ {
433
+ "name": "EventName",
434
+ "type": "FULFILMENT",
435
+ "eventType": "NORMAL",
436
+ "rules": [
437
+ {
438
+ "name": "ACCOUNT.namespace.RuleName",
439
+ "props": { "key": "value" }
440
+ }
441
+ ],
442
+ "triggers": [],
443
+ "userActions": []
444
+ }
445
+ ]
446
+ }
447
+ }
448
+ }
449
+ ```
450
+
451
+ ### Rules
452
+
453
+ - The event `name` **must match** the ruleset `name` inside `meta.workflow`
454
+ - The `entityType` at top level **must match** the ruleset `type`
455
+ - `triggers: []` and `userActions: []` should be left empty
456
+ - Send via the **sync** endpoint: `POST /api/v4.1/event/sync`
457
+ - **Any rule** available in the account can be executed — it does not need to exist in the entity's current deployed workflow
458
+
459
+ ### What Gets Executed
460
+
461
+ | Action | Executed? |
462
+ |--------|-----------|
463
+ | Rules in attached workflow | Yes |
464
+ | Webhooks | Yes |
465
+ | Scheduled events | Yes |
466
+ | Mutations | Yes |
467
+ | Inline (flow control) events | **No** (ignored) |
468
+
469
+ ## MCP Tool: `event.list` — All Filter Parameters
470
+
471
+ ```
472
+ event.list({
473
+ // Event identity
474
+ "eventId": "<EVENT_UUID>", // Filter by specific event ID (alias: id)
475
+
476
+ // Entity filters
477
+ "context.entityRef": "ORDER_REF", // Filter by target entity ref
478
+ "context.entityType": "ORDER", // Filter by target entity type
479
+ "context.entityId": "6611", // Filter by target entity ID
480
+ "context.rootEntityRef": "ORDER_REF", // Filter by root entity ref
481
+ "context.rootEntityType": "ORDER", // Filter by root entity type
482
+ "context.rootEntityId": "6611", // Filter by root entity ID
483
+
484
+ // Aliases (same as context.* equivalents)
485
+ "entityRef": "ORDER_REF", // Alias for context.entityRef
486
+ "entityType": "ORDER", // Alias for context.entityType
487
+
488
+ // Event filters
489
+ "name": "ConfirmValidation", // Filter by event name
490
+ "eventStatus": "FAILED", // Filter by status: FAILED, SUCCESS, COMPLETE, SCHEDULED, PENDING, NO_MATCH
491
+ "eventType": "ORCHESTRATION", // Filter by type: ORCHESTRATION, ORCHESTRATION_AUDIT, API, GENERAL, INTEGRATION, SECURITY
492
+ "category": "exception", // Filter by category: ORDER WORKFLOW, ACTION, ruleSet, rule, exception, snapshot, CUSTOM, BATCH
493
+
494
+ // Time filters
495
+ "from": "2026-02-20T00:00:00Z", // Start of time window (ISO-8601)
496
+ "to": "2026-02-22T23:59:59Z", // End of time window (ISO-8601)
497
+
498
+ // Pagination
499
+ "count": 50, // Page size (1-500 via MCP tool; REST API supports up to 5000)
500
+ "start": 1 // Pagination offset (1-based)
501
+ })
502
+ ```
503
+
504
+ **Multi-value filters:** The REST API accepts multiple values (List) for: `retailerId`, `eventType`, `category`, `context.rootEntityType`, `context.entityType`, `eventStatus`.
505
+
506
+ **Aliases:** `entityRef` = `context.entityRef`, `entityType` = `context.entityType`, `type` = `eventType`, `id` = `eventId`.
507
+
508
+ ## Operational Query Patterns
509
+
510
+ ### Pattern 1: Full Event Timeline for an Entity
511
+
512
+ ```
513
+ event.list({
514
+ "context.entityRef": "<ORDER_REF>",
515
+ "context.entityType": "ORDER",
516
+ "count": 100
517
+ })
518
+ ```
519
+
520
+ ### Pattern 2: All Events Across an Order and Its Children
521
+
522
+ Use `rootEntityRef` to capture ORDER + FULFILMENT + FULFILMENT_OPTION events:
523
+
524
+ ```
525
+ event.list({
526
+ "context.rootEntityRef": "<ORDER_REF>",
527
+ "context.rootEntityType": "ORDER",
528
+ "count": 200
529
+ })
530
+ ```
531
+
532
+ ### Pattern 3: Find Failed Events Across All Entities
533
+
534
+ ```
535
+ event.list({
536
+ "eventStatus": "FAILED",
537
+ "from": "2026-02-22T00:00:00Z",
538
+ "count": 50
539
+ })
540
+ ```
541
+
542
+ ### Pattern 4: Find Exception Details
543
+
544
+ Filter for exception audit events to get stack traces:
545
+
546
+ ```
547
+ event.list({
548
+ "category": "exception",
549
+ "from": "2026-02-22T00:00:00Z",
550
+ "count": 50
551
+ })
552
+ ```
553
+
554
+ ### Pattern 5: Trace What a Specific Event Triggered
555
+
556
+ 1. Get the ORCHESTRATION event:
557
+ ```
558
+ event.get({ "eventId": "<EVENT_ID>" })
559
+ ```
560
+
561
+ 2. Find all audit events it spawned:
562
+ ```
563
+ event.list({
564
+ "eventType": "ORCHESTRATION_AUDIT",
565
+ "context.entityRef": "<ENTITY_REF>",
566
+ "from": "<event_timestamp_minus_1s>",
567
+ "to": "<event_timestamp_plus_30s>",
568
+ "count": 50
569
+ })
570
+ ```
571
+
572
+ Match by checking `sourceEvents` array contains the parent event ID.
573
+
574
+ ### Pattern 6: Find Scheduled (Future-Dated) Events
575
+
576
+ ```
577
+ event.list({
578
+ "eventStatus": "SCHEDULED",
579
+ "context.entityRef": "<ENTITY_REF>",
580
+ "count": 20
581
+ })
582
+ ```
583
+
584
+ ### Pattern 7: Webhook Execution Audit
585
+
586
+ ```
587
+ event.list({
588
+ "eventType": "ORCHESTRATION_AUDIT",
589
+ "category": "ACTION",
590
+ "name": "Send Webhook",
591
+ "context.rootEntityRef": "<ENTITY_REF>",
592
+ "count": 50
593
+ })
594
+ ```
595
+
596
+ Each result's attributes contain: `Request Endpoint`, `Response code`, `Response Body`, `Response Headers`.
597
+
598
+ ### Pattern 7b: Extract Mutation Request Payloads from ACTION Audit
599
+
600
+ Many mutation actions appear with empty/unknown `name`, but still include full dynamic GraphQL request metadata in attributes.
601
+
602
+ ```
603
+ event.list({
604
+ "eventType": "ORCHESTRATION_AUDIT",
605
+ "category": "ACTION",
606
+ "context.rootEntityRef": "<ENTITY_REF>",
607
+ "count": 200
608
+ })
609
+ ```
610
+
611
+ For each ACTION event, inspect:
612
+
613
+ - `attributes.request.queryType` (for example `DynamicUpdateMutation`)
614
+ - `attributes.request.queryName` (for example `updateOrder`, `updateFulfilment`)
615
+ - `attributes.request.queryString`
616
+ - `attributes.request.variables.values.input` (actual mutation payload)
617
+ - `attributes.response.inner` (mutation result entity reference/type)
618
+
619
+ Do not rely on `name` alone for mutation detection.
620
+
621
+ ### Pattern 7c: Future-Dated / Scheduled Evidence from ACTION Payload
622
+
623
+ `eventStatus=SCHEDULED` may not show all delayed dispatches for a root ref. Also inspect ACTION payloads:
624
+
625
+ - `attributes["Future Dated"] == true`
626
+ - `attributes.Event.scheduledOn`
627
+ - `event.get(...).event.meta.scheduledOn`
628
+
629
+ This reveals when delayed events were enqueued and what target entity they were sent to.
630
+
631
+ ### Pattern 8: Rule Execution Timing
632
+
633
+ Find slow-running rulesets by checking `startTimer`/`stopTimer` attributes:
634
+
635
+ ```
636
+ event.list({
637
+ "category": "ruleSet",
638
+ "eventType": "ORCHESTRATION_AUDIT",
639
+ "context.entityRef": "<ENTITY_REF>",
640
+ "count": 100
641
+ })
642
+ ```
643
+
644
+ Calculate duration: `stopTimer - startTimer` (milliseconds).
645
+
646
+ ### Pattern 9: NO_MATCH Events (Workflow Gaps)
647
+
648
+ Find events that didn't match any ruleset — indicates missing workflow configuration:
649
+
650
+ ```
651
+ event.list({
652
+ "eventStatus": "NO_MATCH",
653
+ "from": "2026-02-22T00:00:00Z",
654
+ "count": 50
655
+ })
656
+ ```
657
+
658
+ ### Pattern 10: Log Action Events (Custom Audit)
659
+
660
+ ```
661
+ event.list({
662
+ "category": "CUSTOM",
663
+ "from": "2026-02-22T00:00:00Z",
664
+ "to": "2026-02-22T23:59:59Z",
665
+ "count": 200
666
+ })
667
+ ```
668
+
669
+ ### Pattern 11: Legacy REST API Audit Events
670
+
671
+ ```
672
+ event.list({
673
+ "eventType": "API",
674
+ "name": "POST /api/v4.1/order",
675
+ "count": 100
676
+ })
677
+ ```
678
+
679
+ ### Pattern 12: Paginating Through Large Event Sets
680
+
681
+ ```
682
+ # Page 1
683
+ event.list({ "context.rootEntityRef": "<REF>", "count": 100, "start": 1 })
684
+ # Page 2
685
+ event.list({ "context.rootEntityRef": "<REF>", "count": 100, "start": 101 })
686
+ # Continue while hasMore: true
687
+ ```
688
+
689
+ ## Reading Failed Events — What to Look For
690
+
691
+ ### Exception Event Anatomy
692
+
693
+ ```json
694
+ {
695
+ "name": "com.fluentretail.rubix.exceptions.NotFoundException",
696
+ "type": "ORCHESTRATION_AUDIT",
697
+ "category": "exception",
698
+ "eventStatus": "FAILED",
699
+ "attributes": [
700
+ {
701
+ "name": "exception",
702
+ "type": "OBJECT",
703
+ "value": {
704
+ "code": 404,
705
+ "message": "Workflow for key -1 could not be found...",
706
+ "stackTrace": [...]
707
+ }
708
+ },
709
+ { "name": "lastRule", "value": null },
710
+ { "name": "lastRuleSet", "value": null },
711
+ { "name": "message", "value": "Workflow for key -1 could not be found..." }
712
+ ]
713
+ }
714
+ ```
715
+
716
+ ### Common Exception Patterns
717
+
718
+ | Exception Class | Code | Cause | Fix |
719
+ |----------------|------|-------|-----|
720
+ | `NotFoundException` | 404 | No workflow found for entity type/subtype | Create or deploy workflow matching the `flexType` |
721
+ | `RuleExecutionException` | — | Rule threw during execution | Check `lastRule` and `lastRuleSet`, inspect rule source |
722
+ | `ValidationException` | 400 | Invalid event attributes or entity state | Check required attributes for the event |
723
+ | `AuthorizationException` | 403 | User lacks permission for the operation | Check retailerId scope and user roles |
724
+
725
+ ### Key Fields for Diagnosis
726
+
727
+ | Field | Where | What It Tells You |
728
+ |-------|-------|------------------|
729
+ | `eventStatus` | Event responses | Processing status (SUCCESS, FAILED, COMPLETE, NO_MATCH, etc.) |
730
+ | `attributes.lastRule` | Exception events | Which rule was executing when it failed |
731
+ | `attributes.lastRuleSet` | Exception events | Which ruleset was executing |
732
+ | `attributes.message` | Exception events | Human-readable error description |
733
+ | `attributes.exception.stackTrace` | Exception events | Full Java stack trace for root cause |
734
+ | `source` | Audit events | `<hashcode>.<RulesetName>` — which ruleset generated this audit |
735
+ | `context.sourceEvents` | Audit events | Parent event ID — trace causality chain |
736
+ | `attributes.startTimer` / `stopTimer` | Audit events | Execution timing in epoch ms |
737
+
738
+ ## Event Naming Conventions
739
+
740
+ | Pattern | Example | When Used |
741
+ |---------|---------|-----------|
742
+ | `CREATE` | Auto-generated | Entity creation triggers workflow |
743
+ | `Confirm*` | `ConfirmValidation`, `ConfirmPick` | User-initiated progression actions |
744
+ | `Cancel*` | `CancelOrder`, `CancelFulfilment` | Cancellation actions |
745
+ | `Process*` | `ProcessFulfilment` | Internal workflow progression |
746
+ | `Check*` | `CheckAllFulfilmentsComplete` | Gate/verification events |
747
+ | `*Expiry` | `FulfilmentExpiry` | Scheduled timeout events |
748
+ | `CREATE_Log` | Custom rule output | Log action events (category=CUSTOM) |
749
+ | Rule action names | `Send Event`, `Send Webhook`, `Change State` | ORCHESTRATION_AUDIT names from rule actions |
750
+ | Exception class names | `com.fluentretail.rubix.exceptions.*` | ORCHESTRATION_AUDIT names for exceptions |
751
+ | `snapshot` | Entity snapshot | Snapshot audit events |
752
+
753
+ ## Common Integration Event Patterns
754
+
755
+ Practical event payload templates for common integration scenarios. All use `POST /event/async`.
756
+
757
+ ### Custom Inventory Update
758
+
759
+ When Batch API cannot satisfy inventory requirements:
760
+
761
+ ```json
762
+ {
763
+ "name": "DeltaInventoryUpdate",
764
+ "retailerId": "1",
765
+ "rootEntityType": "INVENTORY_CATALOGUE",
766
+ "rootEntityRef": "INV-CAT-001",
767
+ "entityType": "INVENTORY_POSITION",
768
+ "entityRef": "POS-SKU-LOC-001",
769
+ "attributes": { "skuRef": "SKU-001", "locationRef": "LOC-001", "quantityDelta": 50 }
770
+ }
771
+ ```
772
+
773
+ ### PIM Product Creation
774
+
775
+ External Product Master updates:
776
+
777
+ ```json
778
+ {
779
+ "name": "CreateProduct",
780
+ "retailerId": "1",
781
+ "rootEntityType": "PRODUCT_CATALOGUE",
782
+ "rootEntityRef": "MASTER-CATALOG",
783
+ "entityType": "PRODUCT",
784
+ "entityRef": "PROD-SKU-001",
785
+ "attributes": { "productName": "Widget", "categoryRef": "CAT-001", "brandName": "Brand" }
786
+ }
787
+ ```
788
+
789
+ ### WMS Pick Confirmation
790
+
791
+ Fulfilment updates from Warehouse Management System:
792
+
793
+ ```json
794
+ {
795
+ "name": "ConfirmPick",
796
+ "retailerId": "1",
797
+ "rootEntityType": "ORDER",
798
+ "rootEntityId": "12345",
799
+ "entityType": "FULFILMENT",
800
+ "entityId": "67890",
801
+ "attributes": { "pickedItems": [{ "skuRef": "SKU-001", "quantityPicked": 2 }] }
802
+ }
803
+ ```
804
+
805
+ ### Carrier Tracking Update
806
+
807
+ ```json
808
+ {
809
+ "name": "TrackingUpdate",
810
+ "retailerId": "1",
811
+ "rootEntityType": "ORDER",
812
+ "rootEntityId": "12345",
813
+ "entityType": "CONSIGNMENT",
814
+ "entityId": "11111",
815
+ "attributes": { "trackingNumber": "1Z999AA1234567890", "status": "IN_TRANSIT", "carrierRef": "UPS" }
816
+ }
817
+ ```
818
+
819
+ ## Sync vs Async Event Dispatch
820
+
821
+ | Mode | Endpoint | Behavior | Use When |
822
+ |------|----------|----------|----------|
823
+ | `async` | `/event/async` | Event queued, returns immediately with `{ id }`. Poll `event.list` for result. | Standard workflow events, production use, middleware integrations |
824
+ | `sync` | `/event/sync` | Event processed synchronously. Returns `{ eventId, entityId, eventStatus }`. Blocks until complete. | UI user actions, testing, debugging, run-once workflows |
825
+
826
+ **Async is recommended for all external system integrations.** The sync endpoint is not designed to scale for middleware and should only be used when triggered by a real user action or for operational intervention.
827
+
828
+ **Gotcha:** Async processing time is environment-dependent (often seconds to tens of seconds). After sending, poll `event.list` with entity context and a time window; don't assume immediate completion.
829
+
830
+ ## Cross-Entity Event Tracing
831
+
832
+ To build a complete picture of an order lifecycle across all entities:
833
+
834
+ 1. **Get the order events:**
835
+ ```
836
+ event.list({ "context.rootEntityRef": "<ORDER_REF>", "context.rootEntityType": "ORDER", "count": 200 })
837
+ ```
838
+
839
+ 2. **Group by entityType** — Separate ORDER, FULFILMENT, FULFILMENT_OPTIONS events
840
+
841
+ 3. **Sort by generatedOn** — Build a chronological timeline
842
+
843
+ 4. **Link parent → child** — Match `sourceEvents` arrays to connect triggers to their audit trail
844
+
845
+ 5. **Identify the failure point** — Find the first FAILED or NO_MATCH event in chronological order
846
+
847
+ 6. **Get exception details** — Filter for `category: "exception"` events near the failure timestamp
848
+
849
+ This gives you a complete causal chain: which event triggered which ruleset, which rule action failed, and what exception occurred.
850
+
851
+ ## Event Contracts
852
+
853
+ An event contract is the implicit agreement between an event sender and the rulesets that consume it.
854
+
855
+ ### Inbound Contract (What a Ruleset Expects)
856
+
857
+ - **Props:** Defined by `@ParamString`, `@ParamBoolean` annotations in rule source code
858
+ - **Event attributes:** Read via `DynamicUtils.getJsonPath("event.attributes.byName.<key>")` paths
859
+ - **Entity state:** Some rules assume the entity is in a specific status
860
+
861
+ **How to discover inbound contracts:**
862
+ 1. `plugin.list({ name: "RuleName" })` → check `eventAttributes` field
863
+ 2. `workflow.transitions` → `userActions[].attributes` shows required attributes for user-facing events
864
+ 3. Rule source code: search for `@ParamString` and `getJsonPath("event.*")` patterns
865
+
866
+ ### Outbound Contract (What a Ruleset Produces)
867
+
868
+ - **SendEvent rules** create new ORCHESTRATION events with `sourceEvents` linking back
869
+ - **SetState rules** change entity status, potentially triggering status-trigger rulesets
870
+ - **Mutations** modify entity data
871
+ - **Webhooks** send data to external systems
872
+
873
+ ### Contract Violations
874
+
875
+ | Violation | Symptom | Diagnosis |
876
+ |-----------|---------|-----------|
877
+ | Missing event attribute | Rule reads null, silent failure or exception | Check `plugin.list` eventAttributes vs event.send attributes |
878
+ | Wrong attribute type | ClassCastException in rule execution | Check attribute types match rule expectations |
879
+ | Wrong entity status | Rule guard fails, no state change | Verify entity status matches ruleset trigger |
880
+ | Missing setting | Rule reads null setting value | Cross-reference rule's setting name with `settings` query |
881
+
882
+ ## Integration Patterns
883
+
884
+ Compact patterns for common external system integration event flows.
885
+
886
+ ### Inventory Sync (WMS/ERP)
887
+ - **Entity:** INVENTORY_CATALOGUE or INVENTORY_POSITION
888
+ - **Pattern:** External system → async event → inventory catalogue workflow → UpdateInventoryPosition rule
889
+ - **Key attributes:** sku, quantity, locationRef
890
+ - **Event name:** Varies by implementation (often custom event triggering status-based ruleset)
891
+
892
+ ### Product Sync (PIM)
893
+ - **Entity:** PRODUCT_CATALOGUE
894
+ - **Event name:** `UPSERT_PRODUCT` or custom
895
+ - **Pattern:** PIM → batch ingestion or async event → product catalogue workflow
896
+ - **Key attributes:** productRef, variantRefs, catalogueRef
897
+
898
+ ### Carrier / Shipment Updates
899
+ - **Entity:** FULFILMENT or CONSIGNMENT
900
+ - **Pattern:** Carrier webhook → middleware → async event → fulfilment workflow
901
+ - **Key attributes:** trackingNumber, carrierRef, statusUpdate, deliveredAt
902
+ - **Common events:** TrackingUpdate, DeliveryConfirmed (names vary by implementation)
903
+
904
+ ### Order Intake
905
+ - **Entity:** ORDER
906
+ - **Event name:** `CREATE` (auto-generated on `createOrder` mutation)
907
+ - **Pattern:** Ecommerce platform → createOrder GraphQL mutation → ORDER workflow auto-triggers
908
+ - **Note:** The `CREATE` event is implicit — no explicit event.send needed. The mutation triggers it.
909
+
910
+ ### SendEvent Classification
911
+
912
+ When tracing `SendEvent` rule actions in audit events, classify the dispatch target to understand flow control:
913
+
914
+ | Classification | Detection | Meaning |
915
+ |---------------|-----------|---------|
916
+ | **Same entity** | `sendEvent.entityRef == sourceRule.entityRef` | Internal flow control — triggers another ruleset on the same entity |
917
+ | **Same workflow, different entity** | Same workflow type but different `entityRef` | Internal orchestration — coordinates between entities in the same workflow |
918
+ | **Cross-workflow** | Different workflow type or different `entityType` | External trigger — creates a cascade across workflow boundaries (ORDER→FULFILMENT, FULFILMENT→ARTICLE) |
919
+
920
+ Cross-workflow SendEvents are the primary mechanism for entity lifecycle cascades. When debugging stuck entities, trace these first — a missing or failed cross-workflow SendEvent is the most common cause of child entities never being created.
921
+
922
+ ## Monitoring Patterns
923
+
924
+ ### Business Monitoring
925
+
926
+ Track business execution for entities: why a fulfilment was assigned to a location, why an order moved to exception status, when a fulfilment is stuck too long.
927
+
928
+ | Mechanism | How |
929
+ |-----------|-----|
930
+ | Log action events | Custom rules emit `category=CUSTOM` events; poll via `event.list` |
931
+ | Webhook notifications | Configure webhook rules to notify monitoring systems |
932
+ | GraphQL reporting | Query entities stuck in statuses via `graphql.query` |
933
+ | Event polling | Poll `ORCHESTRATION` events for specific entity/status patterns |
934
+ | Scheduled events | Detect entities stuck in a status for too long via `*Expiry` events |
935
+
936
+ ### Technical Monitoring
937
+
938
+ Track technical issues: rule failures, workflow exceptions, API errors, missing rulesets.
939
+
940
+ | Mechanism | How |
941
+ |-----------|-----|
942
+ | Exception events | Poll `category=exception` with `from/to` for stack traces and rule failure details |
943
+ | NO_MATCH detection | Poll `eventStatus=NO_MATCH` to find events that didn't trigger any ruleset |
944
+ | API error responses | Check REST/GraphQL response codes for failures |
945
+ | Log events | Custom rules can log technical exception details via `context.action().log()` |