@lexq/cli 0.1.39 → 0.1.41
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/AGENTS.md +34 -32
- package/CONTEXT.md +41 -47
- package/README.md +8 -6
- package/dist/index.js +156 -389
- package/dist/mcp/register.js +102 -167
- package/package.json +1 -1
- package/skills/lexq-execution/SKILL.md +120 -65
- package/skills/lexq-groups/SKILL.md +78 -38
- package/skills/lexq-recipes/SKILL.md +122 -149
- package/skills/lexq-rules/SKILL.md +154 -42
- package/skills/lexq-shared/SKILL.md +48 -28
- package/skills/lexq-simulation/SKILL.md +47 -27
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
# 1. Create group
|
|
11
11
|
lexq groups create --json '{
|
|
12
12
|
"name": "tiered-discount",
|
|
13
|
-
"priority": 0,
|
|
14
13
|
"description": "Apply discount based on payment amount tiers"
|
|
15
14
|
}'
|
|
16
15
|
# → Save the group ID
|
|
@@ -23,10 +22,9 @@ lexq versions create --group-id <gid> --json '{"commitMessage": "Initial tiered
|
|
|
23
22
|
lexq facts create --key payment_amount --name "Payment Amount" --type NUMBER --required
|
|
24
23
|
lexq facts create --key customer_tier --name "Customer Tier" --type STRING
|
|
25
24
|
|
|
26
|
-
# 4. Add rules (
|
|
25
|
+
# 4. Add rules — creation order becomes priority order (first created = priority 1 = highest)
|
|
27
26
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
28
27
|
"name": "Premium Tier - 20%",
|
|
29
|
-
"priority": 0,
|
|
30
28
|
"condition": {
|
|
31
29
|
"type": "SINGLE",
|
|
32
30
|
"field": "payment_amount",
|
|
@@ -37,10 +35,10 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
37
35
|
"actions": [{
|
|
38
36
|
"type": "MUTATE_FACT",
|
|
39
37
|
"parameters": {
|
|
40
|
-
"
|
|
38
|
+
"targetVar": "payment_amount",
|
|
41
39
|
"method": "PERCENTAGE",
|
|
42
40
|
"operator": "SUB",
|
|
43
|
-
"
|
|
41
|
+
"operand": 20,
|
|
44
42
|
"rounding": { "mode": "HALF_UP", "scale": 0 }
|
|
45
43
|
}
|
|
46
44
|
}]
|
|
@@ -48,7 +46,6 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
48
46
|
|
|
49
47
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
50
48
|
"name": "Gold Tier - 10%",
|
|
51
|
-
"priority": 1,
|
|
52
49
|
"condition": {
|
|
53
50
|
"type": "GROUP",
|
|
54
51
|
"operator": "AND",
|
|
@@ -60,10 +57,10 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
60
57
|
"actions": [{
|
|
61
58
|
"type": "MUTATE_FACT",
|
|
62
59
|
"parameters": {
|
|
63
|
-
"
|
|
60
|
+
"targetVar": "payment_amount",
|
|
64
61
|
"method": "PERCENTAGE",
|
|
65
62
|
"operator": "SUB",
|
|
66
|
-
"
|
|
63
|
+
"operand": 10,
|
|
67
64
|
"rounding": { "mode": "HALF_UP", "scale": 0 }
|
|
68
65
|
}
|
|
69
66
|
}]
|
|
@@ -71,7 +68,6 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
71
68
|
|
|
72
69
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
73
70
|
"name": "Base Tier - 5%",
|
|
74
|
-
"priority": 2,
|
|
75
71
|
"condition": {
|
|
76
72
|
"type": "SINGLE",
|
|
77
73
|
"field": "payment_amount",
|
|
@@ -82,20 +78,20 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
82
78
|
"actions": [{
|
|
83
79
|
"type": "MUTATE_FACT",
|
|
84
80
|
"parameters": {
|
|
85
|
-
"
|
|
81
|
+
"targetVar": "payment_amount",
|
|
86
82
|
"method": "PERCENTAGE",
|
|
87
83
|
"operator": "SUB",
|
|
88
|
-
"
|
|
84
|
+
"operand": 5,
|
|
89
85
|
"rounding": { "mode": "HALF_UP", "scale": 0 }
|
|
90
86
|
}
|
|
91
87
|
}]
|
|
92
88
|
}'
|
|
93
89
|
|
|
94
90
|
# 5. Validate
|
|
95
|
-
lexq analytics dry-run --version-id <vid> --debug --
|
|
91
|
+
lexq analytics dry-run --version-id <vid> --debug --json '{"facts":{"payment_amount":600000}}'
|
|
96
92
|
# Expected: mutatedFacts.payment_amount = 480000, generatedVariables.payment_amount__delta = -120000
|
|
97
93
|
|
|
98
|
-
lexq analytics dry-run --version-id <vid> --debug --
|
|
94
|
+
lexq analytics dry-run --version-id <vid> --debug --json '{"facts":{"payment_amount":200000}}'
|
|
99
95
|
# Expected: mutatedFacts.payment_amount = 180000, generatedVariables.payment_amount__delta = -20000
|
|
100
96
|
|
|
101
97
|
# 6. Deploy
|
|
@@ -112,7 +108,6 @@ lexq deploy live --group-id <gid> --version-id <vid> --memo "Go live"
|
|
|
112
108
|
```bash
|
|
113
109
|
lexq groups create --json '{
|
|
114
110
|
"name": "fraud-detection",
|
|
115
|
-
"priority": 0,
|
|
116
111
|
"description": "Block suspicious transactions"
|
|
117
112
|
}'
|
|
118
113
|
|
|
@@ -125,7 +120,6 @@ lexq facts create --key country_code --name "Country Code" --type STRING
|
|
|
125
120
|
# High-value + high-frequency
|
|
126
121
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
127
122
|
"name": "High Risk - Large + Frequent",
|
|
128
|
-
"priority": 0,
|
|
129
123
|
"condition": {
|
|
130
124
|
"type": "GROUP",
|
|
131
125
|
"operator": "AND",
|
|
@@ -135,15 +129,13 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
135
129
|
]
|
|
136
130
|
},
|
|
137
131
|
"actions": [
|
|
138
|
-
{ "type": "BLOCK", "parameters": { "reason": "High value + high frequency"
|
|
139
|
-
{ "type": "ADD_TAG", "parameters": { "tag": "fraud_review" } }
|
|
132
|
+
{ "type": "BLOCK", "parameters": { "reason": "High value + high frequency" } }
|
|
140
133
|
]
|
|
141
134
|
}'
|
|
142
135
|
|
|
143
136
|
# Sanctioned country
|
|
144
137
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
145
138
|
"name": "Sanctioned Country Block",
|
|
146
|
-
"priority": 1,
|
|
147
139
|
"condition": {
|
|
148
140
|
"type": "SINGLE",
|
|
149
141
|
"field": "country_code",
|
|
@@ -152,12 +144,12 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
152
144
|
"valueType": "LIST_STRING"
|
|
153
145
|
},
|
|
154
146
|
"actions": [
|
|
155
|
-
{ "type": "BLOCK", "parameters": { "reason": "Sanctioned country"
|
|
147
|
+
{ "type": "BLOCK", "parameters": { "reason": "Sanctioned country" } }
|
|
156
148
|
]
|
|
157
149
|
}'
|
|
158
150
|
|
|
159
151
|
# Validate
|
|
160
|
-
lexq analytics dry-run --version-id <vid> --debug --
|
|
152
|
+
lexq analytics dry-run --version-id <vid> --debug --json '{
|
|
161
153
|
"facts": { "transaction_amount": 10000000, "transaction_count_24h": 15, "country_code": "KR" }
|
|
162
154
|
}'
|
|
163
155
|
```
|
|
@@ -180,17 +172,17 @@ lexq rules update --group-id <gid> --version-id <v2id> --id <ruleId> --json '{
|
|
|
180
172
|
"actions": [{
|
|
181
173
|
"type": "MUTATE_FACT",
|
|
182
174
|
"parameters": {
|
|
183
|
-
"
|
|
175
|
+
"targetVar": "payment_amount",
|
|
184
176
|
"method": "PERCENTAGE",
|
|
185
177
|
"operator": "SUB",
|
|
186
|
-
"
|
|
178
|
+
"operand": 15,
|
|
187
179
|
"rounding": { "mode": "HALF_UP", "scale": 0 }
|
|
188
180
|
}
|
|
189
181
|
}]
|
|
190
182
|
}'
|
|
191
183
|
|
|
192
184
|
# 3. Validate with dry-run
|
|
193
|
-
lexq analytics dry-run --version-id <v2id> --debug --
|
|
185
|
+
lexq analytics dry-run --version-id <v2id> --debug --json '{
|
|
194
186
|
"facts": { "payment_amount": 100000, "customer_tier": "VIP" }
|
|
195
187
|
}'
|
|
196
188
|
|
|
@@ -218,10 +210,13 @@ lexq groups ab-test stop --group-id <gid> --force
|
|
|
218
210
|
|
|
219
211
|
**Goal:** Award loyalty points based on purchase behavior.
|
|
220
212
|
|
|
213
|
+
**Note:** `total_points` must be present in the request facts. The engine is stateless — it does
|
|
214
|
+
not read your database. Send the customer's current balance (or `0` for a new customer) and apply
|
|
215
|
+
the returned value yourself. `MUTATE_FACT` throws if the target fact is absent.
|
|
216
|
+
|
|
221
217
|
```bash
|
|
222
218
|
lexq groups create --json '{
|
|
223
219
|
"name": "loyalty-points",
|
|
224
|
-
"priority": 1,
|
|
225
220
|
"activationMode": "NONE"
|
|
226
221
|
}'
|
|
227
222
|
|
|
@@ -229,12 +224,11 @@ lexq versions create --group-id <gid> --json '{"commitMessage": "Points program
|
|
|
229
224
|
|
|
230
225
|
lexq facts create --key purchase_amount --name "Purchase Amount" --type NUMBER --required
|
|
231
226
|
lexq facts create --key is_first_purchase --name "First Purchase" --type BOOLEAN
|
|
232
|
-
lexq facts create --key total_points --name "Total Points" --type NUMBER
|
|
227
|
+
lexq facts create --key total_points --name "Total Points" --type NUMBER --required
|
|
233
228
|
|
|
234
229
|
# Bonus points for first purchase (fixed 200)
|
|
235
230
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
236
231
|
"name": "First Purchase Bonus Points",
|
|
237
|
-
"priority": 0,
|
|
238
232
|
"condition": {
|
|
239
233
|
"type": "SINGLE",
|
|
240
234
|
"field": "is_first_purchase",
|
|
@@ -244,23 +238,12 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
244
238
|
},
|
|
245
239
|
"actions": [
|
|
246
240
|
{
|
|
247
|
-
"type": "
|
|
241
|
+
"type": "MUTATE_FACT",
|
|
248
242
|
"parameters": {
|
|
249
243
|
"targetVar": "total_points",
|
|
244
|
+
"operator": "ADD",
|
|
250
245
|
"method": "AMOUNT",
|
|
251
|
-
"
|
|
252
|
-
"rounding": { "mode": "HALF_UP", "scale": 0 }
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
"type": "EMIT_NOTIFICATION",
|
|
257
|
-
"parameters": {
|
|
258
|
-
"integrationId": "<notification-integration-uuid>",
|
|
259
|
-
"target": "user_id",
|
|
260
|
-
"notificationPayload": {
|
|
261
|
-
"channel": "PUSH",
|
|
262
|
-
"templateId": "welcome_points"
|
|
263
|
-
}
|
|
246
|
+
"operand": 200
|
|
264
247
|
}
|
|
265
248
|
}
|
|
266
249
|
]
|
|
@@ -269,7 +252,6 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
269
252
|
# Standard points: 0.1% of purchase_amount = 1 point per 1000 KRW
|
|
270
253
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
271
254
|
"name": "Standard Purchase Points",
|
|
272
|
-
"priority": 1,
|
|
273
255
|
"condition": {
|
|
274
256
|
"type": "SINGLE",
|
|
275
257
|
"field": "purchase_amount",
|
|
@@ -279,72 +261,39 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
279
261
|
},
|
|
280
262
|
"actions": [
|
|
281
263
|
{
|
|
282
|
-
"type": "
|
|
264
|
+
"type": "MUTATE_FACT",
|
|
283
265
|
"parameters": {
|
|
284
266
|
"targetVar": "total_points",
|
|
285
267
|
"refVar": "purchase_amount",
|
|
268
|
+
"operator": "ADD",
|
|
286
269
|
"method": "PERCENTAGE",
|
|
287
|
-
"
|
|
270
|
+
"operand": 0.1,
|
|
288
271
|
"rounding": { "mode": "FLOOR", "scale": 0 }
|
|
289
272
|
}
|
|
290
273
|
}
|
|
291
274
|
]
|
|
292
275
|
}'
|
|
293
|
-
```
|
|
294
276
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
**Goal:** Call an external API when a rule matches.
|
|
300
|
-
|
|
301
|
-
```bash
|
|
302
|
-
# 1. Create webhook integration
|
|
303
|
-
lexq integrations save --json '{
|
|
304
|
-
"type": "WEBHOOK",
|
|
305
|
-
"name": "Order Processing Webhook",
|
|
306
|
-
"baseUrl": "https://api.example.com/webhooks/orders",
|
|
307
|
-
"isActive": true
|
|
308
|
-
}'
|
|
309
|
-
|
|
310
|
-
# 2. Use WEBHOOK action in a rule
|
|
311
|
-
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
312
|
-
"name": "Large Order Alert",
|
|
313
|
-
"priority": 0,
|
|
314
|
-
"condition": {
|
|
315
|
-
"type": "SINGLE",
|
|
316
|
-
"field": "order_total",
|
|
317
|
-
"operator": "GREATER_THAN",
|
|
318
|
-
"value": 1000000,
|
|
319
|
-
"valueType": "NUMBER"
|
|
320
|
-
},
|
|
321
|
-
"actions": [
|
|
322
|
-
{
|
|
323
|
-
"type": "EMIT_WEBHOOK",
|
|
324
|
-
"parameters": {
|
|
325
|
-
"url": "https://api.example.com/webhooks/orders",
|
|
326
|
-
"payloadTemplate": {
|
|
327
|
-
"event": "rule_matched",
|
|
328
|
-
"rule": "{{ruleName}}",
|
|
329
|
-
"amount": "{{output.order_total}}"
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
{ "type": "ADD_TAG", "parameters": { "tag": "large_order" } }
|
|
334
|
-
]
|
|
277
|
+
# Verify — new customer, 50,000 KRW purchase → 200 bonus + 50 standard = 250
|
|
278
|
+
lexq analytics dry-run --version-id <vid> --debug --json '{
|
|
279
|
+
"facts": { "purchase_amount": 50000, "is_first_purchase": true, "total_points": 0 }
|
|
335
280
|
}'
|
|
336
281
|
```
|
|
337
282
|
|
|
283
|
+
`total_points` and `refVar: purchase_amount` are two different facts — this is exactly what
|
|
284
|
+
`refVar` exists for. Omitting it would compute `total_points += total_points × 0.1%`.
|
|
285
|
+
|
|
286
|
+
Read `total_points` and `total_points__delta` from `generatedVariables` in the response.
|
|
287
|
+
|
|
338
288
|
---
|
|
339
289
|
|
|
340
|
-
## Recipe
|
|
290
|
+
## Recipe 5: Exclusive Discount (Mutex)
|
|
341
291
|
|
|
342
292
|
**Goal:** Ensure only the best discount applies when multiple rules match.
|
|
343
293
|
|
|
344
294
|
```bash
|
|
345
295
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
346
296
|
"name": "VIP Discount 20%",
|
|
347
|
-
"priority": 0,
|
|
348
297
|
"mutexGroup": "best-discount",
|
|
349
298
|
"mutexMode": "EXCLUSIVE",
|
|
350
299
|
"mutexStrategy": "HIGHEST_PRIORITY",
|
|
@@ -354,10 +303,10 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
354
303
|
"actions": [{
|
|
355
304
|
"type": "MUTATE_FACT",
|
|
356
305
|
"parameters": {
|
|
357
|
-
"
|
|
306
|
+
"targetVar": "payment_amount",
|
|
358
307
|
"method": "PERCENTAGE",
|
|
359
308
|
"operator": "SUB",
|
|
360
|
-
"
|
|
309
|
+
"operand": 20,
|
|
361
310
|
"rounding": { "mode": "HALF_UP", "scale": 0 }
|
|
362
311
|
}
|
|
363
312
|
}]
|
|
@@ -365,7 +314,6 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
365
314
|
|
|
366
315
|
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
367
316
|
"name": "Seasonal Sale 15%",
|
|
368
|
-
"priority": 1,
|
|
369
317
|
"mutexGroup": "best-discount",
|
|
370
318
|
"mutexMode": "EXCLUSIVE",
|
|
371
319
|
"mutexStrategy": "HIGHEST_PRIORITY",
|
|
@@ -375,67 +323,26 @@ lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
|
375
323
|
"actions": [{
|
|
376
324
|
"type": "MUTATE_FACT",
|
|
377
325
|
"parameters": {
|
|
378
|
-
"
|
|
326
|
+
"targetVar": "payment_amount",
|
|
379
327
|
"method": "PERCENTAGE",
|
|
380
328
|
"operator": "SUB",
|
|
381
|
-
"
|
|
329
|
+
"operand": 15,
|
|
382
330
|
"rounding": { "mode": "HALF_UP", "scale": 0 }
|
|
383
331
|
}
|
|
384
332
|
}]
|
|
385
333
|
}'
|
|
386
334
|
|
|
387
|
-
#
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
**Goal:** Issue different coupons by region.
|
|
395
|
-
|
|
396
|
-
```bash
|
|
397
|
-
lexq facts create --key user_region --name "User Region" --type STRING --required
|
|
398
|
-
|
|
399
|
-
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
400
|
-
"name": "Korea Welcome Coupon",
|
|
401
|
-
"priority": 0,
|
|
402
|
-
"condition": {
|
|
403
|
-
"type": "SINGLE", "field": "user_region", "operator": "IN", "value": ["KR"], "valueType": "LIST_STRING"
|
|
404
|
-
},
|
|
405
|
-
"actions": [{
|
|
406
|
-
"type": "EMIT_EVENT",
|
|
407
|
-
"parameters": {
|
|
408
|
-
"integrationId": "<coupon-integration-uuid>",
|
|
409
|
-
"eventPayload": {
|
|
410
|
-
"couponId": "KR_WELCOME_2025",
|
|
411
|
-
"expiryDays": 30
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}]
|
|
415
|
-
}'
|
|
416
|
-
|
|
417
|
-
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
418
|
-
"name": "US Welcome Coupon",
|
|
419
|
-
"priority": 1,
|
|
420
|
-
"condition": {
|
|
421
|
-
"type": "SINGLE", "field": "user_region", "operator": "IN", "value": ["US"], "valueType": "LIST_STRING"
|
|
422
|
-
},
|
|
423
|
-
"actions": [{
|
|
424
|
-
"type": "EMIT_EVENT",
|
|
425
|
-
"parameters": {
|
|
426
|
-
"integrationId": "<coupon-integration-uuid>",
|
|
427
|
-
"eventPayload": {
|
|
428
|
-
"couponId": "US_WELCOME_2025",
|
|
429
|
-
"expiryDays": 14
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}]
|
|
433
|
-
}'
|
|
335
|
+
# Rules are appended in creation order. Confirm the order with `lexq rules list`,
|
|
336
|
+
# or set it explicitly:
|
|
337
|
+
# lexq rules reorder --group-id <gid> --version-id <vid> --rule-ids "<vipRuleId>,<seasonalRuleId>"
|
|
338
|
+
#
|
|
339
|
+
# If a VIP customer pays 50000+, only the 20% VIP discount fires — HIGHEST_PRIORITY
|
|
340
|
+
# picks the rule with the lower priority number within the mutex group.
|
|
434
341
|
```
|
|
435
342
|
|
|
436
343
|
---
|
|
437
344
|
|
|
438
|
-
## Recipe
|
|
345
|
+
## Recipe 6: Version Rollback
|
|
439
346
|
|
|
440
347
|
**Goal:** Something went wrong in production — revert to the previous version.
|
|
441
348
|
|
|
@@ -453,27 +360,37 @@ lexq history stats
|
|
|
453
360
|
|
|
454
361
|
---
|
|
455
362
|
|
|
456
|
-
## Recipe
|
|
363
|
+
## Recipe 7: Monitoring + Failure Triage
|
|
364
|
+
|
|
365
|
+
**Goal:** Check for pending failures and clear the queue.
|
|
457
366
|
|
|
458
|
-
|
|
367
|
+
Failure logs are a dead-letter record for background tasks — platform event webhooks and scheduled
|
|
368
|
+
deployments. There is no retry action: the engine does not re-execute a failed task on request.
|
|
369
|
+
Fix the cause at the source (webhook endpoint, version state), then mark the log.
|
|
459
370
|
|
|
460
371
|
```bash
|
|
461
372
|
# 1. List pending failures
|
|
462
373
|
lexq logs list --status PENDING --page 0 --size 50
|
|
463
374
|
|
|
464
|
-
# 2.
|
|
465
|
-
lexq logs
|
|
375
|
+
# 2. Inspect one to see the original payload and error
|
|
376
|
+
lexq logs get --id <logId>
|
|
466
377
|
|
|
467
|
-
# 3.
|
|
468
|
-
lexq logs bulk-action --ids "
|
|
378
|
+
# 3. Mark as resolved after fixing the cause externally
|
|
379
|
+
lexq logs bulk-action --ids "id1,id2,id3" --action RESOLVE
|
|
469
380
|
|
|
470
|
-
# 4.
|
|
381
|
+
# 4. Mark as intentionally skipped (won't appear in PENDING again)
|
|
382
|
+
lexq logs bulk-action --ids "id4,id5" --action IGNORE
|
|
383
|
+
|
|
384
|
+
# 5. Verify clean state
|
|
471
385
|
lexq logs list --status PENDING --page 0 --size 10
|
|
472
386
|
```
|
|
473
387
|
|
|
388
|
+
`RESOLVE` and `IGNORE` differ only in intent — both remove the log from PENDING. Use `RESOLVE`
|
|
389
|
+
when the underlying problem was fixed, `IGNORE` when it does not need fixing.
|
|
390
|
+
|
|
474
391
|
---
|
|
475
392
|
|
|
476
|
-
## Recipe
|
|
393
|
+
## Recipe 8: Full Policy Migration Workflow
|
|
477
394
|
|
|
478
395
|
**Goal:** Create an entirely new version of a policy with different logic.
|
|
479
396
|
|
|
@@ -490,7 +407,7 @@ lexq rules delete --group-id <gid> --version-id <newVid> --id <obsoleteRuleId> -
|
|
|
490
407
|
lexq rules create --group-id <gid> --version-id <newVid> --json '{...}'
|
|
491
408
|
|
|
492
409
|
# 4. Dry-run test multiple scenarios
|
|
493
|
-
lexq analytics dry-run --version-id <newVid> --debug --
|
|
410
|
+
lexq analytics dry-run --version-id <newVid> --debug --json '{"facts":{...}}'
|
|
494
411
|
|
|
495
412
|
# 5. Run simulation against live baseline
|
|
496
413
|
lexq deploy publish --group-id <gid> --version-id <newVid> --memo "v2 migration"
|
|
@@ -502,4 +419,60 @@ lexq analytics simulation start --json '{
|
|
|
502
419
|
|
|
503
420
|
# 6. If simulation passes, deploy
|
|
504
421
|
lexq deploy live --group-id <gid> --version-id <newVid> --memo "Migration complete"
|
|
505
|
-
```
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Recipe 9: Flag-Based Segmentation
|
|
427
|
+
|
|
428
|
+
**Goal:** Set segment flags that downstream systems read from the decision response.
|
|
429
|
+
|
|
430
|
+
`SET_FACT` assigns a value — it does not append. To accumulate into a `LIST_STRING` fact, send the
|
|
431
|
+
current list in the request facts and assign the full new list. For most segmentation, separate
|
|
432
|
+
boolean or string flags are simpler and easier to query.
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
lexq groups create --json '{"name": "segmentation", "activationMode": "NONE"}'
|
|
436
|
+
lexq versions create --group-id <gid> --json '{"commitMessage": "Segments v1"}'
|
|
437
|
+
|
|
438
|
+
lexq facts create --key lifetime_value --name "Lifetime Value" --type NUMBER --required
|
|
439
|
+
lexq facts create --key signup_days --name "Days Since Signup" --type NUMBER --required
|
|
440
|
+
lexq facts create --key support_tier --name "Support Tier" --type STRING
|
|
441
|
+
lexq facts create --key beta_enabled --name "Beta Access" --type BOOLEAN
|
|
442
|
+
|
|
443
|
+
# High-value customer → priority support
|
|
444
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
445
|
+
"name": "High Value Segment",
|
|
446
|
+
"condition": {
|
|
447
|
+
"type": "SINGLE", "field": "lifetime_value",
|
|
448
|
+
"operator": "GREATER_THAN_OR_EQUAL", "value": 1000000, "valueType": "NUMBER"
|
|
449
|
+
},
|
|
450
|
+
"actions": [
|
|
451
|
+
{ "type": "SET_FACT", "parameters": { "targetVar": "support_tier", "value": "PRIORITY" } }
|
|
452
|
+
]
|
|
453
|
+
}'
|
|
454
|
+
|
|
455
|
+
# Long-tenured customer → beta access
|
|
456
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
457
|
+
"name": "Veteran Beta Access",
|
|
458
|
+
"condition": {
|
|
459
|
+
"type": "SINGLE", "field": "signup_days",
|
|
460
|
+
"operator": "GREATER_THAN", "value": 365, "valueType": "NUMBER"
|
|
461
|
+
},
|
|
462
|
+
"actions": [
|
|
463
|
+
{ "type": "SET_FACT", "parameters": { "targetVar": "beta_enabled", "value": true } }
|
|
464
|
+
]
|
|
465
|
+
}'
|
|
466
|
+
|
|
467
|
+
lexq analytics dry-run --version-id <vid> --debug --json '{
|
|
468
|
+
"facts": { "lifetime_value": 1500000, "signup_days": 400 }
|
|
469
|
+
}'
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Notes**
|
|
473
|
+
|
|
474
|
+
- `SET_FACT` creates the fact if it does not exist. `MUTATE_FACT` requires the target to already
|
|
475
|
+
be present — that is the division of labor between them.
|
|
476
|
+
- Flags appear in `generatedVariables` in the response. `SET_FACT` produces no `__delta` because
|
|
477
|
+
it is an assignment, not an arithmetic change.
|
|
478
|
+
- Segment on a `LIST_STRING` fact with `HAS_ANY` / `HAS_ALL` / `HAS_NONE`, not `CONTAINS`.
|