@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,695 @@
1
+ # Feature Plan Template
2
+
3
+ > **Source of truth** for all implementation plans generated by planning-gated skills.
4
+ > Referenced from individual skill Planning Gate sections.
5
+ > Enhance this file to improve all future plans across all skills.
6
+
7
+ ---
8
+
9
+ ## Universal Source Classification
10
+
11
+ Every table row in every section of a plan carries a **Source** column. This makes plans self-contained — a reviewer can scan any section and immediately see what's new (risky, needs review), what's reused (lower risk, proven pattern), and what's unchanged (context only).
12
+
13
+ | Label | Meaning | Required Detail |
14
+ |-------|---------|----------------|
15
+ | **NEW** | Doesn't exist yet, must be created | Full pseudo logic / value shape / trigger spec |
16
+ | **EXISTING** | Already deployed, no modification needed | State current value, confirm no change |
17
+ | **MODIFIED** | Already exists but needs changes | Show before → after diff |
18
+ | **REUSED** | Pattern exists in codebase, copy the approach | Reference source file/rule |
19
+ | **OOTB** | Platform rule, use with configuration only | State which props/values to configure |
20
+
21
+ ---
22
+
23
+ ## Plan File Conventions
24
+
25
+ **Account-scoped path:** `accounts/<PROFILE>/plans/<YYYY-MM-DD>-<skill-short-name>-<brief-slug>.md`
26
+
27
+ **Repo-scoped path:** `.fluent-ai-skills/plans/<YYYY-MM-DD>-<skill-short-name>-<brief-slug>.md`
28
+
29
+ Use account-scoped when targeting a Fluent environment. Use repo-scoped for package-internal work.
30
+
31
+ **Naming rules:**
32
+ - `<PROFILE>` — active Fluent CLI profile (e.g., `HMDEV`, `SAGIRISH`)
33
+ - `<YYYY-MM-DD>` — creation date
34
+ - `<skill-short-name>` — generating skill without `fluent-` prefix (e.g., `feature-plan`, `rule-scaffold`, `workflow-builder`)
35
+ - `<brief-slug>` — 2-4 word kebab-case description (e.g., `curbside-pickup`, `order-hd-returns`)
36
+
37
+ **Status lifecycle:** `PENDING` → `APPROVED` / `REJECTED` → implementation → session summary references plan file.
38
+
39
+ ---
40
+
41
+ ## Template
42
+
43
+ Every plan file MUST follow this structure. Omit sections marked _optional_ when they don't apply. Sections marked **required** must always be present.
44
+
45
+ ````markdown
46
+ # Plan: <Feature Title>
47
+
48
+ | Field | Value |
49
+ |-------|-------|
50
+ | Status | `PENDING` |
51
+ | Created | <YYYY-MM-DD> |
52
+ | Skill | `/fluent-<skill-name>` |
53
+ | Profile | <PROFILE> |
54
+ | Retailer(s) | <RETAILER_REF> (ID: <N>) |
55
+ | Module(s) | <module names if applicable> |
56
+ | Approved by | --- |
57
+ | Revision | 1 |
58
+
59
+ ## Table of Contents
60
+
61
+ 1. [Business Context](#1-business-context)
62
+ 2. [Scope](#2-scope)
63
+ 3. [Architecture & Diagrams](#3-architecture--diagrams)
64
+ 4. [Workflows](#4-workflows)
65
+ 5. [Statuses](#5-statuses)
66
+ 6. [Rulesets — Change Summary](#6-rulesets--change-summary)
67
+ - 6.1 [Complete Workflow Inventory](#61-complete-workflow-inventory) (full rules + props)
68
+ 7. [Rules Inventory](#7-rules-inventory)
69
+ 8. [Detailed Design (New Rules)](#8-detailed-design-new-rules)
70
+ 9. [Settings](#9-settings)
71
+ 10. [Webhooks](#10-webhooks)
72
+ 11. [Scheduled Events](#11-scheduled-events)
73
+ 12. [GraphQL Operations](#12-graphql-operations)
74
+ 13. [Cross-Entity Impact](#13-cross-entity-impact)
75
+ 14. [Files](#14-files)
76
+ 15. [Risks & Mitigations](#15-risks--mitigations)
77
+ 16. [Test Plan](#16-test-plan)
78
+ 17. [Deployment Sequence](#17-deployment-sequence)
79
+ 18. [Rollback Plan](#18-rollback-plan)
80
+
81
+ ---
82
+
83
+ ## 1. Business Context **[required]**
84
+
85
+ > **Business objective:** <What business problem does this solve?>
86
+ > **Trigger:** <What triggered this — user story, bug, go-live requirement?>
87
+ > **Success criteria:** <How do we know this is done? Measurable outcome.>
88
+
89
+ ## 2. Scope **[required]**
90
+
91
+ <One paragraph: what this plan achieves technically. Reference specific workflow names, entity types, and module names.>
92
+
93
+ **In scope:**
94
+ - <bulleted list>
95
+
96
+ **Out of scope:**
97
+ - <bulleted list>
98
+
99
+ ## 3. Architecture & Diagrams **[required — at least one diagram]**
100
+
101
+ ### 3.1 State Machine
102
+
103
+ > Mermaid `stateDiagram-v2` for workflow creation or modification.
104
+ > Show ALL statuses. Annotate: `:::added`, `:::removed`, `:::modified`.
105
+
106
+ ```mermaid
107
+ stateDiagram-v2
108
+ [*] --> CREATED
109
+ CREATED --> BOOKED : ConfirmOrder
110
+ BOOKED --> AWAITING_PICKUP : CurbsideRequested:::added
111
+ AWAITING_PICKUP --> COMPLETE : ConfirmPickup:::added
112
+ AWAITING_PICKUP --> CANCELLED : CurbsideTimeout:::added
113
+ BOOKED --> FULFILLED : FulfilmentComplete
114
+ ```
115
+
116
+ ### 3.2 Cross-Entity Event Flow
117
+
118
+ > Mermaid `sequenceDiagram` for cross-entity events, webhooks, or multi-step processing.
119
+
120
+ ```mermaid
121
+ sequenceDiagram
122
+ participant WF as Workflow Engine
123
+ participant R1 as CreateCurbsidePickup [NEW]
124
+ participant R2 as ValidatePickupWindow [NEW]
125
+ participant GQL as GraphQL API
126
+ participant EXT as External Webhook
127
+
128
+ WF->>R2: run(context) [ORDER]
129
+ R2->>GQL: SettingUtils.getSettingByRef(curbside.pickup.config)
130
+ GQL-->>R2: {maxPickupHours: 48}
131
+ R2->>R2: Validate window
132
+ WF->>R1: run(context) [ORDER]
133
+ R1->>GQL: query order items
134
+ GQL-->>R1: OrderItem[]
135
+ R1->>GQL: createFulfilment(type=CURBSIDE)
136
+ GQL-->>R1: Fulfilment created
137
+ WF->>EXT: POST /curbside/notify (via SendWebhook OOTB)
138
+ ```
139
+
140
+ ### 3.3 Data Flow _(optional)_
141
+
142
+ > Mermaid `flowchart` for complex processing, decision trees, or event propagation chains.
143
+
144
+ ### 3.4 Before → After Diff _(optional)_
145
+
146
+ > For modifications to existing workflows. Use `workflow.diff` output or describe changes.
147
+
148
+ ### 3.5 Entity Relationships _(optional)_
149
+
150
+ > For module scaffolding or retailer config. Show entity types and their edges.
151
+
152
+ ### 3.6 Workflow Processing Flow **[required when modifying an existing workflow]**
153
+
154
+ > Mermaid `flowchart TD` showing the complete ruleset→status processing chain for each modified workflow.
155
+ > Highlights NEW and MODIFIED rulesets with green styling. Shows how new rulesets fit into the existing flow.
156
+ > This complements the state machine (§3.1) which shows statuses only — this shows the rulesets that drive transitions.
157
+
158
+ ```mermaid
159
+ flowchart TD
160
+ subgraph "ORDER (order lifecycle)"
161
+ O1[CREATED] -->|CREATE| O2[ON_VALIDATION]
162
+ O2 -->|ValidateOrder| O2
163
+ O2 -->|"ConfirmValidation + ProcessOrder"| O3[IN_PROGRESS]
164
+ O3 -->|NotifyFulfilmentShipped| O4[SHIPPED]
165
+ O4 -->|"NotifyFulfilmentDelivered + TransitionToCompleted"| O5[COMPLETED]
166
+ end
167
+ subgraph "FULFILMENT_CHOICE (FC routing)"
168
+ FC1[FC CREATED] -->|RouteFulfilmentChoice| FC2{Route by type}
169
+ FC2 -->|HD| FC3[ProcessHDFC]
170
+ FC2 -->|CC| FC4[ProcessCCFC]
171
+ FC2 -->|CURBSIDE| FC5[ProcessCurbsideFC]:::added
172
+ end
173
+ subgraph "FULFILMENT (lifecycle by subtype)"
174
+ F1[FUL CREATED] -->|AssignFulfilment| F2[READY]
175
+ F2 -->|ConfirmAllocation| F3[RECEIVED]
176
+ F3 -->|CreateInvoice| F4[INVOICED]
177
+ F4 -->|ConfirmPick| F5[PICKPACK]
178
+ F5 -->|"ReadyForPickup (CURBSIDE)"| F6[READY_FOR_PICKUP]:::added
179
+ F6 -->|"ConfirmCollection (CURBSIDE)"| F7[COLLECTED]:::added
180
+ end
181
+
182
+ classDef added fill:#90EE90,stroke:#333
183
+ ```
184
+
185
+ > **Format guidance:**
186
+ > - One `subgraph` per entity type in the workflow
187
+ > - Node labels show status names; edge labels show ruleset/event names
188
+ > - Use `:::added` class for NEW rulesets/statuses, bold edge labels for MODIFIED
189
+ > - Keep it high-level — show the routing and status flow, not individual rules within rulesets
190
+ > - This diagram pairs with §6.1 Complete Workflow Inventory which shows the detail per ruleset
191
+
192
+ ## 4. Workflows **[required]**
193
+
194
+ | Workflow | Source | Current Ver | Action | Details |
195
+ |----------|--------|-------------|--------|---------|
196
+ | ORDER::HD | MODIFIED | v12 | Modify | Add CurbsidePickupRequested ruleset, add 2 statuses |
197
+ | FULFILMENT::CURBSIDE | NEW | — | Create | New workflow for curbside fulfilments |
198
+ | FULFILMENT::HD | EXISTING | v8 | — | No change — listed for context |
199
+
200
+ ## 5. Statuses **[required when touching workflows]**
201
+
202
+ | Status | Source | Entity Type | Workflow | Added By |
203
+ |--------|--------|-------------|----------|----------|
204
+ | CREATED | EXISTING | ORDER | ORDER::HD | Already in workflow |
205
+ | BOOKED | EXISTING | ORDER | ORDER::HD | Already in workflow |
206
+ | AWAITING_PICKUP | NEW | ORDER | ORDER::HD | SetState in CurbsidePickupRequested ruleset |
207
+ | PICKED_UP | NEW | ORDER | ORDER::HD | SetState in ConfirmCurbsidePickup ruleset |
208
+ | READY_FOR_PICKUP | NEW | FULFILMENT | FULFILMENT::CURBSIDE | SetState in PrepareCurbside ruleset |
209
+ | COLLECTED | NEW | FULFILMENT | FULFILMENT::CURBSIDE | SetState in ConfirmCollection ruleset |
210
+
211
+ ## 6. Rulesets — Change Summary **[required when touching workflows]**
212
+
213
+ > Quick-glance table of what rulesets are being added, modified, or referenced. Full detail (every rule, every prop) lives in §6.1.
214
+
215
+ | Ruleset | Source | Workflow | From Status | To Status | Change Summary |
216
+ |---------|--------|----------|-------------|-----------|----------------|
217
+ | CurbsidePickupRequested | NEW | ORDER::HD | BOOKED | AWAITING_PICKUP | 3 rules: ValidatePickupWindow (NEW), CreateCurbsidePickupRule (NEW), SendEvent (OOTB) |
218
+ | ConfirmCurbsidePickup | NEW | ORDER::HD | AWAITING_PICKUP | PICKED_UP | 2 rules: UpdateFulfilmentStatus (EXISTING), SetOrderStatus (OOTB) |
219
+ | BookOrder | EXISTING | ORDER::HD | CREATED | BOOKED | No change — listed for context |
220
+
221
+ ### 6.1 Complete Workflow Inventory **[required when modifying an existing workflow]**
222
+
223
+ > The **single source of truth** for the entire workflow. Lists ALL rulesets — not just new/modified — with every rule and every prop value.
224
+ > This replaces the need to read the raw workflow JSON. Reviewers can see exactly where new rulesets fit, what's already there, and what each rule does.
225
+
226
+ **Format:** One block per ruleset, grouped by entity type. Each block shows:
227
+ - **Heading:** `# | RulesetName | Source marker` — bold for NEW/MODIFIED
228
+ - **Metadata:** Entity type, subtype (if filtered), trigger status, event type
229
+ - **Description:** What the ruleset does
230
+ - **Rules table:** Every rule in execution order with full prop values and source annotations
231
+ - **User actions:** If the ruleset exposes UI buttons
232
+
233
+ **Source markers:** `EXISTING` = no change, `MODIFIED` = changed by this plan (show what changed), `NEW` = added by this plan
234
+
235
+ ---
236
+
237
+ #### ORDER Rulesets (N existing, N new, N modified)
238
+
239
+ ##### 1. CREATE | EXISTING
240
+ **Entity:** ORDER | **Trigger:** CREATED | **EventType:** NORMAL
241
+ > Order created. Create missing products, send webhook, branch on fraud.
242
+
243
+ | # | Rule | Props | Source |
244
+ |---|------|-------|--------|
245
+ | 1 | CreateMissingVariantProducts | — | EXISTING |
246
+ | 2 | SendWebhookWithDynamicAttributes | `setting: webhook.order.created` | EXISTING |
247
+ | 3 | SendEventOnVerifyingAttributeValue | `attributeName: fraudCheckRequired, onMatchEventName: InitFraudCheck, noMatchEventName: SkipFraudCheck` | EXISTING |
248
+
249
+ ---
250
+
251
+ ##### 2. BookOrder | EXISTING
252
+ **Entity:** ORDER | **Trigger:** BOOKED | **EventType:** NORMAL
253
+ > Standard booking, no change.
254
+
255
+ | # | Rule | Props | Source |
256
+ |---|------|-------|--------|
257
+ | 1 | ... | ... | EXISTING |
258
+
259
+ ---
260
+
261
+ ##### 3. **CurbsidePickupRequested** | **NEW**
262
+ **Entity:** ORDER | **Trigger:** BOOKED | **EventType:** NORMAL
263
+ > Validates curbside request (time window, vehicle desc) and creates CURBSIDE fulfilment.
264
+
265
+ | # | Rule | Props | Source |
266
+ |---|------|-------|--------|
267
+ | 1 | **ValidatePickupWindow** | `configSettingName: curbside.pickup.config, pickupTimePath: event.requestedPickupTime, vehicleDescPath: event.vehicleDescription` | **NEW** |
268
+ | 2 | **CreateCurbsidePickupRule** | `fulfilmentType: CURBSIDE, pickupLocationPath: event.pickupLocationRef` | **NEW** |
269
+ | 3 | SendEvent | `eventName: FulfilmentCreated` | OOTB |
270
+
271
+ **User Actions:**
272
+ ```json
273
+ [{"eventName": "CurbsidePickupRequested", "context": [{"label": "REQUEST CURBSIDE PICKUP", "type": "PRIMARY"}]}]
274
+ ```
275
+
276
+ ---
277
+
278
+ > _Repeat for every ruleset in the workflow. Use `...` only for large EXISTING blocks where props are unchanged and well-known. Never abbreviate NEW or MODIFIED rulesets._
279
+
280
+ #### Summary
281
+
282
+ | Entity | Existing | New | Modified | Total |
283
+ |--------|----------|-----|----------|-------|
284
+ | ORDER | N | N | N | N |
285
+ | FULFILMENT_CHOICE | N | N | N | N |
286
+ | FULFILMENT | N | N | N | N |
287
+ | **Total** | **N** | **N** | **N** | **N** |
288
+
289
+ > **Key rules for this section:**
290
+ > - **Every ruleset gets a block** — no exceptions. This is the complete workflow.
291
+ > - **Every rule in every ruleset gets a row** with its full props.
292
+ > - **Props must show actual values**, not placeholders. For EXISTING rulesets, read from the workflow JSON.
293
+ > - **NEW and MODIFIED blocks are bolded** in the heading and in rule rows.
294
+ > - **MODIFIED rulesets** must show what changed — add a note like "Added rule #3 (NEW)" or "Changed prop `type` from `CC` to `CURBSIDE`".
295
+ > - **Group by entity type** with entity-level count headers.
296
+ > - **User actions** are shown as JSON blocks below the rules table (only if the ruleset has them).
297
+ > - This section replaces the need to cross-reference §6 and §6.1 — it is the single comprehensive reference.
298
+
299
+ ## 7. Rules Inventory **[required]**
300
+
301
+ > ALL rules involved in the feature. Every rule classified by source.
302
+ > A feature typically involves a mix of OOTB, existing custom, and new custom rules.
303
+
304
+ | ID | Rule Name | Source | Module / Plugin | Entity | Inline/Scheduled | Purpose |
305
+ |----|-----------|--------|----------------|--------|-----------------|---------|
306
+ | R1 | CreateCurbsidePickupRule | **NEW** | sagirish-extensions | ORDER | Inline | Creates CURBSIDE fulfilment with pickup location |
307
+ | R2 | ValidatePickupWindow | **NEW** | sagirish-extensions | ORDER | Inline | Validates pickup time is within allowed window |
308
+ | R3 | SendEvent | **OOTB** | fc-core | ORDER | Inline | Fires CurbsidePickupCreated → FULFILMENT. Config: `eventName=CurbsidePickupCreated` |
309
+ | R4 | SetAttributes | **OOTB** | fc-core | FULFILMENT | Inline | Sets estimatedPickupTime from event attribute. Config: `attributeMap={...}` |
310
+ | R5 | SendWebhook | **OOTB** | fc-plugin-webhook | ORDER | Inline | HTTP POST to curbside notification endpoint. Config: `settingName=webhook.curbside.notify` |
311
+ | R6 | ScheduleExpiry | **OOTB** | fc-plugin-order | ORDER | Inline | Schedules CurbsideTimeout event. Config: `delay=30m, eventName=CurbsideTimeout` |
312
+ | R7 | UpdateFulfilmentStatus | **EXISTING** | sagirish-extensions | FULFILMENT | Inline | Reused from HD flow — no code changes needed |
313
+ | R8 | SetOrderStatus | **OOTB** | fc-core | ORDER | Inline | Sets ORDER to target status. Config: `status=AWAITING_PICKUP` |
314
+
315
+ > **For OOTB rules:** state the prop/config values to set in the ruleset.
316
+ > **For EXISTING rules:** confirm no code changes needed, or if MODIFIED, note what changes.
317
+ > **For NEW rules:** full pseudo logic in §8.
318
+
319
+ ## 8. Detailed Design (New Rules) **[required if any NEW rules exist]**
320
+
321
+ > For each **NEW** rule in §7, provide: class metadata, parameters table, pseudo logic, and GraphQL operations.
322
+ > Skip this section entirely if all rules are OOTB or EXISTING.
323
+
324
+ ---
325
+
326
+ ### R1: CreateCurbsidePickupRule
327
+
328
+ **Class:** `com.fluentcommerce.rule.order.CreateCurbsidePickupRule`
329
+ **Extends:** `BaseRule`
330
+ **Entity:** ORDER
331
+
332
+ #### Parameters
333
+
334
+ | Param | Type | Required | Default | Description |
335
+ |-------|------|----------|---------|-------------|
336
+ | pickupLocationPath | String | Yes | — | JSON path to pickup location ref |
337
+ | estimatedPickupTimePath | String | No | — | JSON path to estimated pickup time |
338
+ | fulfilmentType | String | No | `CURBSIDE` | Fulfilment subtype |
339
+ | vehicleDescriptionPath | String | No | — | JSON path to vehicle description |
340
+
341
+ #### Pseudo Logic
342
+
343
+ ```
344
+ FUNCTION run(context):
345
+ order = context.getEntity()
346
+ event = context.getEvent()
347
+
348
+ // 1. Extract required fields
349
+ pickupLocationRef = extractFromPath(event, props.pickupLocationPath)
350
+ IF pickupLocationRef IS NULL:
351
+ THROW "Pickup location ref is required but was null at path: {pickupLocationPath}"
352
+
353
+ // 2. Extract optional fields
354
+ estimatedPickupTime = extractFromPath(event, props.estimatedPickupTimePath) // nullable
355
+ vehicleDescription = extractFromPath(event, props.vehicleDescriptionPath) // nullable
356
+
357
+ // 3. Fetch order items
358
+ items = DynamicUtils.queryList(context, "items", OrderItem.class, null)
359
+ IF items IS EMPTY:
360
+ THROW "Order has no items — cannot create fulfilment"
361
+
362
+ // 4. Build fulfilment
363
+ fulfilmentRef = "CURB-{order.ref}-{pickupLocationRef}"
364
+ fulfilmentInput = CreateFulfilmentInput {
365
+ ref: fulfilmentRef,
366
+ type: props.fulfilmentType OR "CURBSIDE",
367
+ deliveryType: "CURBSIDE",
368
+ order: { id: order.id },
369
+ fromLocation: { ref: pickupLocationRef },
370
+ items: items.map(i => { ref: i.ref, quantity: i.quantity }),
371
+ attributes: [
372
+ { name: "pickupLocationRef", value: pickupLocationRef },
373
+ { name: "estimatedPickupTime", value: estimatedPickupTime },
374
+ { name: "vehicleDescription", value: vehicleDescription },
375
+ { name: "deliveryType", value: "CURBSIDE" },
376
+ { name: "createdByRule", value: "CreateCurbsidePickupRule" }
377
+ ]
378
+ }
379
+
380
+ // 5. Execute mutation
381
+ DynamicUtils.create(context, fulfilmentInput)
382
+
383
+ // 6. Log
384
+ context.addLog("Created curbside fulfilment {fulfilmentRef} for order {order.ref}")
385
+ ```
386
+
387
+ #### GraphQL Operations
388
+
389
+ **Query — Fetch order items:** _(REUSED pattern from CreateFulfilmentFromSourcingLocation)_
390
+ ```graphql
391
+ query {
392
+ orderById(id: $orderId) {
393
+ items { edges { node { id ref quantity productRef } } }
394
+ }
395
+ }
396
+ ```
397
+
398
+ **Mutation — Create fulfilment:** _(REUSED pattern)_
399
+ ```graphql
400
+ mutation($input: CreateFulfilmentInput!) {
401
+ createFulfilment(input: $input) { id ref status }
402
+ }
403
+ ```
404
+
405
+ ---
406
+
407
+ ### R2: ValidatePickupWindow
408
+
409
+ **Class:** `com.fluentcommerce.rule.order.ValidatePickupWindowRule`
410
+ **Extends:** `BaseRule`
411
+ **Entity:** ORDER
412
+
413
+ #### Parameters
414
+
415
+ | Param | Type | Required | Default | Description |
416
+ |-------|------|----------|---------|-------------|
417
+ | configSettingName | String | Yes | — | Setting key for pickup config JSON |
418
+ | pickupTimePath | String | Yes | — | JSON path to requested pickup time in event |
419
+
420
+ #### Pseudo Logic
421
+
422
+ ```
423
+ FUNCTION run(context):
424
+ order = context.getEntity()
425
+ event = context.getEvent()
426
+
427
+ // 1. Read config from setting
428
+ config = SettingUtils.getSettingByRef(context, props.configSettingName)
429
+ maxPickupHours = config.lobValue.maxPickupHours
430
+
431
+ // 2. Extract requested pickup time from event
432
+ requestedPickupTime = extractFromPath(event, props.pickupTimePath)
433
+ IF requestedPickupTime IS NULL:
434
+ THROW "Requested pickup time is required at path: {pickupTimePath}"
435
+
436
+ // 3. Validate window
437
+ orderCreatedOn = order.createdOn
438
+ deadline = orderCreatedOn + maxPickupHours hours
439
+ IF requestedPickupTime > deadline:
440
+ THROW "Pickup time {requestedPickupTime} exceeds allowed window of {maxPickupHours}h from order creation"
441
+
442
+ // 4. Pass — validation gate, no mutation
443
+ context.addLog("Order {order.ref} passed pickup window validation")
444
+ ```
445
+
446
+ #### GraphQL Operations
447
+
448
+ **Query — Read setting:** _(REUSED pattern — SettingUtils utility)_
449
+ ```graphql
450
+ query {
451
+ settings(first: 1, filter: { name: { eq: $settingName }, context: { eq: "RETAILER" } }) {
452
+ edges { node { lobValue } }
453
+ }
454
+ }
455
+ ```
456
+
457
+ ---
458
+
459
+ > _Repeat this pattern for each NEW rule._
460
+
461
+ ## 9. Settings **[required when creating/modifying settings]**
462
+
463
+ | Setting Key | Source | Context | Context ID | Value Type | Shape / Example | Used By (Rule) |
464
+ |------------|--------|---------|-----------|-----------|-----------------|---------------|
465
+ | curbside.pickup.config | **NEW** | RETAILER | 5 | LOB | `{"maxPickupHours": 48, "zones": ["ZONE_A", "ZONE_B"], "requireVehicleDesc": true}` | R2: ValidatePickupWindow |
466
+ | webhook.curbside.notify | **NEW** | RETAILER | 5 | LOB | `{"url": "https://store-api.example.com/curbside/notify", "headers": {"X-Api-Key": "..."}}` | R5: SendWebhook |
467
+ | ORDER_HD_FULFILMENT_TYPE | **EXISTING** | RETAILER | 5 | STRING | Already exists, value `"HD"` — no change | — |
468
+
469
+ > **For NEW settings:** full JSON example showing all fields.
470
+ > **For EXISTING settings:** confirm current value and whether it needs modification.
471
+ > **For MODIFIED settings:** show before → after diff.
472
+
473
+ ## 10. Webhooks _(optional)_
474
+
475
+ | Setting Name | Source | Method | Endpoint Pattern | Trigger Rule | Payload Shape |
476
+ |-------------|--------|--------|-----------------|-------------|---------------|
477
+ | webhook.curbside.notify | **NEW** | POST | `${url}` from setting | R5: SendWebhook (OOTB) | `{"orderRef": "...", "pickupTime": "...", "locationRef": "...", "vehicleDesc": "..."}` |
478
+
479
+ > Omit this section if no webhooks are involved.
480
+
481
+ ## 11. Scheduled Events _(optional)_
482
+
483
+ | Event Name | Source | Delay | Entity Type | Triggered By (Rule) | Purpose |
484
+ |-----------|--------|-------|-------------|-------------------|---------|
485
+ | CurbsideTimeout | **NEW** | 30min | ORDER | R6: ScheduleExpiry (OOTB) | Cancel pickup if not collected |
486
+
487
+ > Omit this section if no scheduled events are involved.
488
+
489
+ ## 12. GraphQL Operations **[required when rules make GraphQL calls]**
490
+
491
+ | Operation | Source | Type | Root Field | Selected Fields | Used By (Rule) | Purpose |
492
+ |-----------|--------|------|-----------|----------------|---------------|---------|
493
+ | Fetch order items | **REUSED** pattern from CreateFulfilmentFromSourcingLocation | Query | orderById.items | id, ref, quantity | R1: CreateCurbsidePickupRule | Get items for fulfilment creation |
494
+ | Create fulfilment | **REUSED** pattern | Mutation | createFulfilment | ref, type, order, items, attributes | R1: CreateCurbsidePickupRule | Create CURBSIDE fulfilment |
495
+ | Read setting | **REUSED** — SettingUtils | Query | settings | lobValue | R2: ValidatePickupWindow | Read pickup config |
496
+
497
+ > **"REUSED pattern"** means existing code in the codebase already does this — copy the approach.
498
+ > **"NEW"** would mean a novel GraphQL operation not seen in the codebase.
499
+
500
+ ## 13. Cross-Entity Impact **[required for multi-entity features]**
501
+
502
+ | Source Entity | Target Entity | Mechanism | Source | Action |
503
+ |--------------|--------------|-----------|--------|--------|
504
+ | ORDER | FULFILMENT | createFulfilment mutation | **NEW** (in R1: CreateCurbsidePickupRule) | Creates CURBSIDE fulfilment |
505
+ | ORDER | FULFILMENT | SendEvent (OOTB R3) | **REUSED** | Fires CurbsidePickupCreated |
506
+ | FULFILMENT | ORDER | SendEvent (OOTB) | **REUSED** | Status update event back to ORDER |
507
+
508
+ > Omit this section for single-entity features.
509
+
510
+ ## 14. Files **[required]**
511
+
512
+ | # | Action | Path | Source | Description |
513
+ |---|--------|------|--------|-------------|
514
+ | 1 | Create | `.../src/main/java/.../CreateCurbsidePickupRule.java` | NEW | Rule R1 |
515
+ | 2 | Create | `.../src/test/java/.../CreateCurbsidePickupRuleTest.java` | NEW | Tests for R1 (4 tests) |
516
+ | 3 | Create | `.../src/main/java/.../ValidatePickupWindowRule.java` | NEW | Rule R2 |
517
+ | 4 | Create | `.../src/test/java/.../ValidatePickupWindowRuleTest.java` | NEW | Tests for R2 (3 tests) |
518
+ | 5 | Modify | `.../resources/module.json` | MODIFIED | Wire R1, R2 registrations |
519
+ | 6 | Modify | `accounts/.../workflows/.../ORDER__HD.json` | MODIFIED | Add CurbsidePickupRequested + ConfirmCurbsidePickup rulesets |
520
+ | 7 | Create | `accounts/.../workflows/.../FULFILMENT__CURBSIDE.json` | NEW | New curbside fulfilment workflow |
521
+
522
+ > All paths relative to `accounts/<PROFILE>/SOURCE/<repo>/`.
523
+
524
+ ## 15. Risks & Mitigations **[required]**
525
+
526
+ | Risk | Severity | Impact | Mitigation |
527
+ |------|----------|--------|-----------|
528
+ | Ruleset trigger overlaps with existing OnCreate | HIGH | Events route to wrong ruleset | Verify trigger uniqueness via workflow-analyzer |
529
+ | New rule throws on null pickup location | MEDIUM | Order stuck in current status | Unit test null/empty inputs; validation guard |
530
+ | Webhook endpoint not provisioned at go-live | LOW | Notification silently fails | Setting updated later; log warning |
531
+
532
+ ## 16. Test Plan **[required]**
533
+
534
+ | # | Test | Type | Event | Expected Status | Assertion |
535
+ |---|------|------|-------|----------------|-----------|
536
+ | 1 | Happy path: curbside pickup | E2E | CurbsidePickupRequested | ORDER → AWAITING_PICKUP | status + fulfilment created |
537
+ | 2 | Pickup collected | E2E | ConfirmCurbsidePickup | ORDER → PICKED_UP | status + fulfilment COLLECTED |
538
+ | 3 | Timeout cancellation | E2E | (wait 30min) | ORDER → CANCELLED | scheduled event fired |
539
+ | 4 | Regression: normal HD | E2E | CreateOrder | ORDER → BOOKED | Existing flow unaffected |
540
+ | 5 | R1: creates fulfilment | Unit | — | — | Mutation called with correct args |
541
+ | 6 | R1: throws on missing location | Unit | — | — | RuntimeException |
542
+ | 7 | R2: rejects expired window | Unit | — | — | RuntimeException |
543
+ | 8 | R2: passes valid pickup time | Unit | — | — | No exception, log written |
544
+
545
+ ## 17. Deployment Sequence **[required for environment changes]**
546
+
547
+ | # | Step | Depends On | Skill | Rollback |
548
+ |---|------|-----------|-------|----------|
549
+ | 1 | Validate module structure | — | `/fluent-module-validate` | N/A |
550
+ | 2 | Build module v1.2.4 | 1 | `/fluent-build` | N/A (local) |
551
+ | 3 | Pre-deploy check | 2 | `/fluent-pre-deploy-check` | N/A |
552
+ | 4 | Deploy module | 3 (READY) | `/fluent-module-deploy` | Re-deploy v1.2.3 |
553
+ | 5 | Upload ORDER::HD v13 | 4 | `/fluent-workflow-deploy` | Re-upload v12 |
554
+ | 6 | Upload FULFILMENT::CURBSIDE v1 | 4 | `/fluent-workflow-deploy` | Delete workflow |
555
+ | 7 | Create settings (2) | 5, 6 | `/fluent-settings` | Delete settings |
556
+ | 8 | E2E test | 7 | `/fluent-e2e-test` | — |
557
+
558
+ > **Ordering constraint:** Module deploy (step 4) must complete before workflow deploy (steps 5-6). Workflows reference rules that are registered during module deployment. Deploying workflows before the module causes NO_MATCH events.
559
+
560
+ ## 18. Rollback Plan _(optional — recommended for production changes)_
561
+
562
+ > How to reverse this change:
563
+ > 1. Re-upload ORDER::HD v12 via `/fluent-workflow-deploy`
564
+ > 2. Delete FULFILMENT::CURBSIDE workflow (or leave dormant -- no triggers will route to it)
565
+ > 3. Delete/disable webhook and config settings
566
+ > 4. Re-deploy module v1.2.3 via `/fluent-module-deploy`
567
+ > 5. Note: New statuses (AWAITING_PICKUP, PICKED_UP) become dormant (no triggers route to them)
568
+ ````
569
+
570
+ ---
571
+
572
+ ## Section Applicability Guide
573
+
574
+ Not every plan needs every section. Use this guide:
575
+
576
+ | Section | When Required |
577
+ |---------|-------------|
578
+ | 1. Business Context | **Always** |
579
+ | 2. Scope | **Always** |
580
+ | 3. Architecture & Diagrams | **Always** (at least one diagram) |
581
+ | 4. Workflows | **Always** (even "No workflow changes" is valid) |
582
+ | 5. Statuses | When adding or modifying workflow statuses |
583
+ | 6. Rulesets — Change Summary | **Always** when touching workflows (quick-glance table of what's changing) |
584
+ | 6.1 Complete Workflow Inventory | When modifying an existing workflow. **Full rules + props for every ruleset** — the single source of truth |
585
+ | 7. Rules Inventory | **Always** when the feature involves rules |
586
+ | 8. Detailed Design (New Rules) | When any rule is **NEW** |
587
+ | 9. Settings | When creating/modifying settings |
588
+ | 10. Webhooks | When adding webhook-based rules |
589
+ | 11. Scheduled Events | When adding time-delayed events |
590
+ | 12. GraphQL Operations | When rules make GraphQL calls |
591
+ | 13. Cross-Entity Impact | When feature spans multiple entity types |
592
+ | 14. Files | **Always** |
593
+ | 15. Risks | **Always** |
594
+ | 16. Test Plan | **Always** |
595
+ | 17. Deployment Sequence | When deploying to an environment |
596
+ | 18. Rollback Plan | Recommended for production; optional for dev/sandbox |
597
+
598
+ ## Rules Classification Decision Flow
599
+
600
+ Before classifying any rule, follow this decision tree:
601
+
602
+ 1. **Search `plugin.list`** for an OOTB match by name or description
603
+ 2. **If OOTB exists and fits** → label as **OOTB**, document the prop values to configure
604
+ 3. **If OOTB exists but doesn't quite fit** → document why. Consider wrapping (OOTB + custom pre/post rule) vs writing from scratch
605
+ 4. **Search existing custom source** (`accounts/<PROFILE>/SOURCE/`) for a similar rule
606
+ 5. **If existing custom fits** → label as **EXISTING**, confirm no changes needed
607
+ 6. **If existing custom needs tweaks** → label as **MODIFIED**, show before → after
608
+ 7. **If pattern exists** but no reusable rule → label the approach as **REUSED** in GraphQL Operations (§12)
609
+ 8. **Only when no viable alternative exists** → label as **NEW**, provide full pseudo logic in §8
610
+
611
+ ## Diagram Requirements
612
+
613
+ Plans MUST include visual Mermaid diagrams. Include ALL that apply:
614
+
615
+ | Diagram | When Required | Mermaid Type |
616
+ |---------|-------------|-------------|
617
+ | State machine | Any workflow creation or modification | `stateDiagram-v2` |
618
+ | Cross-entity event flow | Cross-entity events, webhooks, multi-step processing | `sequenceDiagram` |
619
+ | Data flow | Complex processing, decision trees, event propagation | `flowchart` |
620
+ | Before → After diff | Modifications to existing workflows | Text or `workflow.diff` |
621
+ | Entity relationships | Module scaffolding, retailer config | `erDiagram` or `flowchart` |
622
+ | Workflow processing flow | Modifying an existing workflow with multiple rulesets | `flowchart TD` |
623
+
624
+ For workflow modifications, the state diagram MUST show ALL statuses (not just new ones) and annotate changes with `:::added`, `:::removed`, `:::modified`. The workflow processing flow (§3.6) MUST show all rulesets in execution context with NEW/MODIFIED highlighted.
625
+
626
+ **Syntax validation:** Before writing any plan file, validate all Mermaid blocks against `/fluent-mermaid-validate` and the Mermaid Syntax Rules below.
627
+
628
+ ## Mermaid Syntax Rules
629
+
630
+ **MANDATORY: Validate every Mermaid block against these rules before outputting.**
631
+
632
+ ### All Diagrams
633
+ - First line after the mermaid fence MUST be the diagram type (`stateDiagram-v2`, `sequenceDiagram`, `flowchart TD`, etc.) — no blank line between fence and type
634
+ - Node/state IDs: alphanumeric and underscores only. NO spaces, NO hyphens, NO special chars. Use `as` aliasing for display names
635
+ - Every `subgraph`, `alt`, `opt`, `loop`, `par`, `state { }` block MUST have a matching `end`
636
+ - No HTML tags in labels (use `<br/>` only in flowchart node labels, nowhere else)
637
+ - No trailing semicolons
638
+
639
+ ### stateDiagram-v2
640
+ - MUST use `stateDiagram-v2` (NOT `stateDiagram` — v1 has different syntax)
641
+ - Start/end: `[*]` (brackets required)
642
+ - Transitions: `StateA --> StateB : EventName` (double dash + arrow)
643
+ - WRONG: `->` (single arrow), `=>`, `-->` without space before `:`
644
+ - Composite: `state "Display Name" as alias`
645
+ - Notes: `note right of StateName : text`
646
+ - Style annotations: `StateName:::className` (three colons)
647
+
648
+ ### sequenceDiagram
649
+ - Declare participants: `participant A as "Display Name"` — MUST appear before first use
650
+ - Messages: `A->>B: text` (solid request), `A-->>B: text` (dashed response), `A-)B: text` (async)
651
+ - WRONG: `A->B` (renders as different arrow type, usually not what you want)
652
+ - WRONG: `A-->B` (this is a dotted line without arrowhead)
653
+ - Activations: `activate A` / `deactivate A` on separate lines, OR `A->>+B:` / `B-->>-A:`
654
+ - Blocks: `alt condition` / `else other` / `end` — every block needs `end`
655
+ - Notes: `Note over A,B: text` or `Note right of A: text` (capital N)
656
+
657
+ ### flowchart
658
+ - MUST declare direction: `flowchart TD` (top-down), `flowchart LR` (left-right), `flowchart RL`, `flowchart BT`
659
+ - WRONG: `flowchart` alone (no direction = parse error), `graph TD` (deprecated, use `flowchart`)
660
+ - Node shapes: `A[rect]`, `A(rounded)`, `A{diamond}`, `A((circle))`, `A>flag]`, `A([stadium])`, `A[[subroutine]]`
661
+ - Arrows: `-->` solid, `-.->` dotted, `==>` thick
662
+ - Labels on arrows: `A -->|label text| B` (pipes around label)
663
+ - WRONG: `A --> label --> B`, `A --label--> B`
664
+ - Subgraphs: `subgraph Title` ... `end` — can be nested
665
+ - Node IDs MUST NOT start with a digit or contain spaces: `node1` OK, `1node` BAD, `my node` BAD
666
+
667
+ ### classDiagram
668
+ - Class: `class ClassName { +String field; +method() }` — visibility prefixes: `+` public, `-` private, `#` protected
669
+ - Relationships: `A *-- B` composition, `A o-- B` aggregation, `A --> B` dependency, `A --|> B` inheritance
670
+
671
+ ### erDiagram
672
+ - Entities: plain names `ORDER`, `FULFILMENT` (no brackets)
673
+ - Relationships: `ORDER ||--o{ FULFILMENT : "has"` — cardinality markers: `||` exactly one, `o|` zero-or-one, `}|` one-or-more, `}o` zero-or-more
674
+ - WRONG: using flowchart syntax, missing relationship label string
675
+
676
+ ### Pre-Output Checklist
677
+ Before including any mermaid block in output:
678
+ 1. Diagram type on first line? (no blank line after fence)
679
+ 2. All IDs alphanumeric/underscore only?
680
+ 3. All blocks closed with `end`?
681
+ 4. Arrow syntax correct for this diagram type?
682
+ 5. Participant/node declarations before first use?
683
+ 6. No mixing of syntax from different diagram types?
684
+
685
+ ## Plan Lifecycle
686
+
687
+ 1. **Create** — Agent writes plan file with `Status: PENDING`, `Revision: 1`. Creates the plans directory if needed.
688
+ 2. **Present** — Agent shows the full plan content to the user and waits for approval.
689
+ 3. **Approve/Reject** — User responds:
690
+ - **Approved** ("yes", "go ahead", "approved", "do it") → update `Status: APPROVED`, `Approved by: user`, proceed
691
+ - **Rejected with feedback** → increment `Revision`, revise content, reset `Status: PENDING`, re-present
692
+ - **Questions** → answer without changing the file. If answers lead to changes, revise and increment revision
693
+ - **"Just do it" / "skip planning"** → write `Status: APPROVED (auto-skip)`, `Approved by: user (gate skipped)`, proceed
694
+ 4. **Reference** — During implementation, the plan file is the single source of truth for what was agreed.
695
+ 5. **Post-implementation** — Session summary references the plan file path. Future sessions can read approved plans.