@lexq/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +117 -0
- package/CONTEXT.md +158 -0
- package/README.md +153 -0
- package/dist/index.js +1850 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/skills/lexq-execution/SKILL.md +211 -0
- package/skills/lexq-groups/SKILL.md +181 -0
- package/skills/lexq-recipes/SKILL.md +407 -0
- package/skills/lexq-rules/SKILL.md +264 -0
- package/skills/lexq-shared/SKILL.md +139 -0
- package/skills/lexq-simulation/SKILL.md +279 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# LexQ CLI — Policy Groups
|
|
2
|
+
|
|
3
|
+
> **Prerequisite:** Read `lexq-shared/SKILL.md` first.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
A **Policy Group** is the top-level container. It holds versions (each containing rules), manages deployment state, and controls conflict resolution when multiple groups interact.
|
|
8
|
+
|
|
9
|
+
## Status Lifecycle
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
ACTIVE ──→ DISABLED (emergency stop, re-enable anytime)
|
|
13
|
+
│ │
|
|
14
|
+
└──→ ARCHIVED ←┘ (permanent, cannot be restored)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- **ACTIVE**: Normal operation. Executions are processed.
|
|
18
|
+
- **DISABLED**: All executions are blocked. Use for emergency stop. Can re-enable by updating status back to ACTIVE.
|
|
19
|
+
- **ARCHIVED**: Permanently removed from execution. Cannot be undone.
|
|
20
|
+
|
|
21
|
+
## CRUD Commands
|
|
22
|
+
|
|
23
|
+
### List Groups
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
lexq groups list --page 0 --size 20
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Get Group Detail
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
lexq groups get --id <groupId>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Returns full detail including `activationMode`, `activationStrategy`, `executionLimit`, and A/B test state.
|
|
36
|
+
|
|
37
|
+
### Create Group
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
lexq groups create --json '{
|
|
41
|
+
"name": "discount-policy",
|
|
42
|
+
"description": "VIP discount rules",
|
|
43
|
+
"priority": 0,
|
|
44
|
+
"activationMode": "NONE",
|
|
45
|
+
"activationStrategy": "FIRST_MATCH",
|
|
46
|
+
"status": "ACTIVE"
|
|
47
|
+
}'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Required fields:** `name`, `priority`
|
|
51
|
+
|
|
52
|
+
**Optional fields with defaults:**
|
|
53
|
+
|
|
54
|
+
| Field | Default | Description |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `activationMode` | `NONE` | Conflict resolution mode |
|
|
57
|
+
| `activationStrategy` | `FIRST_MATCH` | Strategy within the mode |
|
|
58
|
+
| `executionLimit` | `null` | Max rules that can fire |
|
|
59
|
+
| `activationGroup` | `null` | Logical group for cross-group conflict resolution |
|
|
60
|
+
| `status` | `ACTIVE` | Initial status |
|
|
61
|
+
|
|
62
|
+
### Update Group
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
lexq groups update --id <groupId> --json '{
|
|
66
|
+
"name": "updated-discount-policy",
|
|
67
|
+
"priority": 1
|
|
68
|
+
}'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Only include fields you want to change. Omitted fields are not modified.
|
|
72
|
+
|
|
73
|
+
### Delete Group
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
lexq groups delete --id <groupId>
|
|
77
|
+
# Prompts for confirmation. Use --force to skip.
|
|
78
|
+
lexq groups delete --id <groupId> --force
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Warning:** Deleting a group cascades — all versions, rules, and deployment history are removed.
|
|
82
|
+
|
|
83
|
+
## Conflict Resolution
|
|
84
|
+
|
|
85
|
+
### Activation Mode (across groups in the same `activationGroup`)
|
|
86
|
+
|
|
87
|
+
| Mode | Behavior |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `NONE` | All matching rules fire. No conflict resolution. |
|
|
90
|
+
| `EXCLUSIVE` | Only one group wins within the activation group. |
|
|
91
|
+
| `MAX_N` | Up to `executionLimit` groups can fire. |
|
|
92
|
+
|
|
93
|
+
### Activation Strategy
|
|
94
|
+
|
|
95
|
+
| Strategy | Behavior |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `FIRST_MATCH` | First matching group by priority wins. |
|
|
98
|
+
| `HIGHEST_PRIORITY` | Lowest priority number wins (0 = highest). |
|
|
99
|
+
| `MAX_BENEFIT` | Group producing the largest action value wins. |
|
|
100
|
+
|
|
101
|
+
**Constraint:** All groups sharing the same `activationGroup` **must** use identical `activationMode` and `activationStrategy`. The API rejects mismatches with `ACTIVATION_CONFIG_MISMATCH`.
|
|
102
|
+
|
|
103
|
+
### Example: Exclusive Discount Groups
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Only one of these can fire per execution
|
|
107
|
+
lexq groups create --json '{
|
|
108
|
+
"name": "vip-discount",
|
|
109
|
+
"priority": 0,
|
|
110
|
+
"activationGroup": "discounts",
|
|
111
|
+
"activationMode": "EXCLUSIVE",
|
|
112
|
+
"activationStrategy": "HIGHEST_PRIORITY"
|
|
113
|
+
}'
|
|
114
|
+
|
|
115
|
+
lexq groups create --json '{
|
|
116
|
+
"name": "seasonal-discount",
|
|
117
|
+
"priority": 1,
|
|
118
|
+
"activationGroup": "discounts",
|
|
119
|
+
"activationMode": "EXCLUSIVE",
|
|
120
|
+
"activationStrategy": "HIGHEST_PRIORITY"
|
|
121
|
+
}'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## A/B Testing
|
|
125
|
+
|
|
126
|
+
Split traffic between the current live version and a test version.
|
|
127
|
+
|
|
128
|
+
### Start A/B Test
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
lexq groups ab-test start --group-id <groupId> --version-id <publishedVersionId> --traffic-rate 30
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`--traffic-rate` is the percentage (1–99) of traffic routed to the test version.
|
|
135
|
+
|
|
136
|
+
### Adjust Traffic
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
lexq groups ab-test adjust --group-id <groupId> --traffic-rate 50
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Stop A/B Test
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
lexq groups ab-test stop --group-id <groupId>
|
|
146
|
+
# Prompts for confirmation. Use --force to skip.
|
|
147
|
+
lexq groups ab-test stop --group-id <groupId> --force
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
This reverts all traffic to the main version. The test version remains ACTIVE but is no longer receiving traffic.
|
|
151
|
+
|
|
152
|
+
### A/B Test Workflow
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
1. Create two versions (v1 live, v2 DRAFT with changes)
|
|
156
|
+
2. Publish v2: lexq deploy publish --group-id <gid> --version-id <v2id>
|
|
157
|
+
3. Start A/B: lexq groups ab-test start --group-id <gid> --version-id <v2id> --traffic-rate 10
|
|
158
|
+
4. Monitor: lexq history stats (compare metrics)
|
|
159
|
+
5. Adjust traffic gradually: 10% → 30% → 50% → 100%
|
|
160
|
+
6. Promote winner: lexq deploy live --group-id <gid> --version-id <v2id>
|
|
161
|
+
7. Stop test: lexq groups ab-test stop --group-id <gid> --force
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Common Patterns
|
|
165
|
+
|
|
166
|
+
### Check Before Creating
|
|
167
|
+
|
|
168
|
+
Always list existing groups first to avoid duplicates:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
lexq groups list --format json | # parse and check if name exists
|
|
172
|
+
lexq groups create --json '...'
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Emergency Stop
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
lexq groups update --id <groupId> --json '{"status": "DISABLED"}'
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
This immediately stops all executions for the group. No undeploy needed.
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# LexQ CLI — Recipes
|
|
2
|
+
|
|
3
|
+
> **Prerequisite:** Read `lexq-shared/SKILL.md` first. Each recipe is a complete, copy-paste workflow.
|
|
4
|
+
|
|
5
|
+
## Recipe 1: Tiered Discount Policy
|
|
6
|
+
|
|
7
|
+
**Goal:** Apply different discounts based on payment amount.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 1. Create group
|
|
11
|
+
lexq groups create --json '{
|
|
12
|
+
"name": "tiered-discount",
|
|
13
|
+
"priority": 0,
|
|
14
|
+
"description": "Apply discount based on payment amount tiers"
|
|
15
|
+
}'
|
|
16
|
+
# → Save the group ID
|
|
17
|
+
|
|
18
|
+
# 2. Create DRAFT version
|
|
19
|
+
lexq versions create --group-id <gid> --json '{"commitMessage": "Initial tiered discount"}'
|
|
20
|
+
# → Save the version ID
|
|
21
|
+
|
|
22
|
+
# 3. Register facts (skip if already exist)
|
|
23
|
+
lexq facts create --key payment_amount --name "Payment Amount" --type NUMBER --required
|
|
24
|
+
lexq facts create --key customer_tier --name "Customer Tier" --type STRING
|
|
25
|
+
|
|
26
|
+
# 4. Add rules (highest priority first)
|
|
27
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
28
|
+
"name": "Premium Tier - 20%",
|
|
29
|
+
"priority": 0,
|
|
30
|
+
"condition": {
|
|
31
|
+
"type": "SINGLE",
|
|
32
|
+
"field": "payment_amount",
|
|
33
|
+
"operator": "GREATER_THAN_OR_EQUAL",
|
|
34
|
+
"value": 500000,
|
|
35
|
+
"valueType": "NUMBER"
|
|
36
|
+
},
|
|
37
|
+
"actions": [{
|
|
38
|
+
"type": "DISCOUNT",
|
|
39
|
+
"parameters": { "method": "PERCENTAGE", "rate": 20, "referenceFactKey": "payment_amount" }
|
|
40
|
+
}]
|
|
41
|
+
}'
|
|
42
|
+
|
|
43
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
44
|
+
"name": "Gold Tier - 10%",
|
|
45
|
+
"priority": 1,
|
|
46
|
+
"condition": {
|
|
47
|
+
"type": "GROUP",
|
|
48
|
+
"operator": "AND",
|
|
49
|
+
"children": [
|
|
50
|
+
{ "type": "SINGLE", "field": "payment_amount", "operator": "GREATER_THAN_OR_EQUAL", "value": 100000, "valueType": "NUMBER" },
|
|
51
|
+
{ "type": "SINGLE", "field": "payment_amount", "operator": "LESS_THAN", "value": 500000, "valueType": "NUMBER" }
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"actions": [{
|
|
55
|
+
"type": "DISCOUNT",
|
|
56
|
+
"parameters": { "method": "PERCENTAGE", "rate": 10, "referenceFactKey": "payment_amount" }
|
|
57
|
+
}]
|
|
58
|
+
}'
|
|
59
|
+
|
|
60
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
61
|
+
"name": "Base Tier - 5%",
|
|
62
|
+
"priority": 2,
|
|
63
|
+
"condition": {
|
|
64
|
+
"type": "SINGLE",
|
|
65
|
+
"field": "payment_amount",
|
|
66
|
+
"operator": "GREATER_THAN_OR_EQUAL",
|
|
67
|
+
"value": 30000,
|
|
68
|
+
"valueType": "NUMBER"
|
|
69
|
+
},
|
|
70
|
+
"actions": [{
|
|
71
|
+
"type": "DISCOUNT",
|
|
72
|
+
"parameters": { "method": "PERCENTAGE", "rate": 5, "referenceFactKey": "payment_amount" }
|
|
73
|
+
}]
|
|
74
|
+
}'
|
|
75
|
+
|
|
76
|
+
# 5. Validate
|
|
77
|
+
lexq analytics dry-run --version-id <vid> --debug --mock --json '{"facts":{"payment_amount":600000}}'
|
|
78
|
+
# Expected: 20% discount → 120000
|
|
79
|
+
|
|
80
|
+
lexq analytics dry-run --version-id <vid> --debug --mock --json '{"facts":{"payment_amount":200000}}'
|
|
81
|
+
# Expected: 10% discount → 20000
|
|
82
|
+
|
|
83
|
+
# 6. Deploy
|
|
84
|
+
lexq deploy publish --group-id <gid> --version-id <vid> --memo "Tiered discount v1"
|
|
85
|
+
lexq deploy live --group-id <gid> --version-id <vid> --memo "Go live"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Recipe 2: Fraud Detection / Transaction Block
|
|
91
|
+
|
|
92
|
+
**Goal:** Block suspicious transactions based on multiple signals.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
lexq groups create --json '{
|
|
96
|
+
"name": "fraud-detection",
|
|
97
|
+
"priority": 0,
|
|
98
|
+
"description": "Block suspicious transactions"
|
|
99
|
+
}'
|
|
100
|
+
|
|
101
|
+
lexq versions create --group-id <gid> --json '{"commitMessage": "Fraud rules v1"}'
|
|
102
|
+
|
|
103
|
+
lexq facts create --key transaction_amount --name "Transaction Amount" --type NUMBER --required
|
|
104
|
+
lexq facts create --key transaction_count_24h --name "Transactions in 24h" --type NUMBER
|
|
105
|
+
lexq facts create --key country_code --name "Country Code" --type STRING
|
|
106
|
+
|
|
107
|
+
# High-value + high-frequency
|
|
108
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
109
|
+
"name": "High Risk - Large + Frequent",
|
|
110
|
+
"priority": 0,
|
|
111
|
+
"condition": {
|
|
112
|
+
"type": "GROUP",
|
|
113
|
+
"operator": "AND",
|
|
114
|
+
"children": [
|
|
115
|
+
{ "type": "SINGLE", "field": "transaction_amount", "operator": "GREATER_THAN", "value": 5000000, "valueType": "NUMBER" },
|
|
116
|
+
{ "type": "SINGLE", "field": "transaction_count_24h", "operator": "GREATER_THAN", "value": 10, "valueType": "NUMBER" }
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"actions": [
|
|
120
|
+
{ "type": "BLOCK", "parameters": { "reason": "High value + high frequency", "code": "FRAUD_HIGH_RISK" } },
|
|
121
|
+
{ "type": "ADD_TAG", "parameters": { "tag": "fraud_review" } }
|
|
122
|
+
]
|
|
123
|
+
}'
|
|
124
|
+
|
|
125
|
+
# Sanctioned country
|
|
126
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
127
|
+
"name": "Sanctioned Country Block",
|
|
128
|
+
"priority": 1,
|
|
129
|
+
"condition": {
|
|
130
|
+
"type": "SINGLE",
|
|
131
|
+
"field": "country_code",
|
|
132
|
+
"operator": "IN",
|
|
133
|
+
"value": ["XX", "YY", "ZZ"],
|
|
134
|
+
"valueType": "LIST_STRING"
|
|
135
|
+
},
|
|
136
|
+
"actions": [
|
|
137
|
+
{ "type": "BLOCK", "parameters": { "reason": "Sanctioned country", "code": "COUNTRY_BLOCKED" } }
|
|
138
|
+
]
|
|
139
|
+
}'
|
|
140
|
+
|
|
141
|
+
# Validate
|
|
142
|
+
lexq analytics dry-run --version-id <vid> --debug --mock --json '{
|
|
143
|
+
"facts": { "transaction_amount": 10000000, "transaction_count_24h": 15, "country_code": "KR" }
|
|
144
|
+
}'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Recipe 3: A/B Test a Rule Change
|
|
150
|
+
|
|
151
|
+
**Goal:** Test a discount rate change (10% → 15%) with gradual traffic rollout.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Current live version is v1 with 10% discount.
|
|
155
|
+
|
|
156
|
+
# 1. Clone the live version to create a new DRAFT
|
|
157
|
+
lexq versions clone --group-id <gid> --version-id <v1id>
|
|
158
|
+
# → Save the new version ID (v2)
|
|
159
|
+
|
|
160
|
+
# 2. Update the discount rule in v2
|
|
161
|
+
lexq rules update --group-id <gid> --version-id <v2id> --id <ruleId> --json '{
|
|
162
|
+
"actions": [{
|
|
163
|
+
"type": "DISCOUNT",
|
|
164
|
+
"parameters": { "method": "PERCENTAGE", "rate": 15, "referenceFactKey": "payment_amount" }
|
|
165
|
+
}]
|
|
166
|
+
}'
|
|
167
|
+
|
|
168
|
+
# 3. Validate with dry-run
|
|
169
|
+
lexq analytics dry-run --version-id <v2id> --debug --mock --json '{
|
|
170
|
+
"facts": { "payment_amount": 100000, "customer_tier": "VIP" }
|
|
171
|
+
}'
|
|
172
|
+
|
|
173
|
+
# 4. Publish v2
|
|
174
|
+
lexq deploy publish --group-id <gid> --version-id <v2id> --memo "15% discount test"
|
|
175
|
+
|
|
176
|
+
# 5. Start A/B test at 10% traffic
|
|
177
|
+
lexq groups ab-test start --group-id <gid> --version-id <v2id> --traffic-rate 10
|
|
178
|
+
|
|
179
|
+
# 6. Monitor (check execution stats periodically)
|
|
180
|
+
lexq history stats
|
|
181
|
+
|
|
182
|
+
# 7. Gradually increase: 10% → 30% → 50%
|
|
183
|
+
lexq groups ab-test adjust --group-id <gid> --traffic-rate 30
|
|
184
|
+
lexq groups ab-test adjust --group-id <gid> --traffic-rate 50
|
|
185
|
+
|
|
186
|
+
# 8. If v2 wins, promote to 100%
|
|
187
|
+
lexq deploy live --group-id <gid> --version-id <v2id> --memo "15% discount winner"
|
|
188
|
+
lexq groups ab-test stop --group-id <gid> --force
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Recipe 4: Point Reward Program
|
|
194
|
+
|
|
195
|
+
**Goal:** Award loyalty points based on purchase behavior.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
lexq groups create --json '{
|
|
199
|
+
"name": "loyalty-points",
|
|
200
|
+
"priority": 1,
|
|
201
|
+
"activationMode": "NONE"
|
|
202
|
+
}'
|
|
203
|
+
|
|
204
|
+
lexq versions create --group-id <gid> --json '{"commitMessage": "Points program v1"}'
|
|
205
|
+
|
|
206
|
+
lexq facts create --key purchase_amount --name "Purchase Amount" --type NUMBER --required
|
|
207
|
+
lexq facts create --key is_first_purchase --name "First Purchase" --type BOOLEAN
|
|
208
|
+
|
|
209
|
+
# Double points for first purchase
|
|
210
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
211
|
+
"name": "First Purchase Double Points",
|
|
212
|
+
"priority": 0,
|
|
213
|
+
"condition": {
|
|
214
|
+
"type": "SINGLE",
|
|
215
|
+
"field": "is_first_purchase",
|
|
216
|
+
"operator": "EQUALS",
|
|
217
|
+
"value": true,
|
|
218
|
+
"valueType": "BOOLEAN"
|
|
219
|
+
},
|
|
220
|
+
"actions": [
|
|
221
|
+
{ "type": "POINT", "parameters": { "amount": 200, "pointType": "BONUS" } },
|
|
222
|
+
{ "type": "NOTIFICATION", "parameters": { "channel": "PUSH", "template": "welcome_points" } }
|
|
223
|
+
]
|
|
224
|
+
}'
|
|
225
|
+
|
|
226
|
+
# Standard points (1 point per 1000 KRW)
|
|
227
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
228
|
+
"name": "Standard Purchase Points",
|
|
229
|
+
"priority": 1,
|
|
230
|
+
"condition": {
|
|
231
|
+
"type": "SINGLE",
|
|
232
|
+
"field": "purchase_amount",
|
|
233
|
+
"operator": "GREATER_THAN_OR_EQUAL",
|
|
234
|
+
"value": 1000,
|
|
235
|
+
"valueType": "NUMBER"
|
|
236
|
+
},
|
|
237
|
+
"actions": [
|
|
238
|
+
{ "type": "SET_FACT", "parameters": { "key": "points_earned", "value": "purchase_amount / 1000" } }
|
|
239
|
+
]
|
|
240
|
+
}'
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Recipe 5: Webhook Integration
|
|
246
|
+
|
|
247
|
+
**Goal:** Call an external API when a rule matches.
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# 1. Create webhook integration
|
|
251
|
+
lexq integrations save --json '{
|
|
252
|
+
"type": "WEBHOOK",
|
|
253
|
+
"name": "Order Processing Webhook",
|
|
254
|
+
"baseUrl": "https://api.example.com/webhooks/orders",
|
|
255
|
+
"isActive": true
|
|
256
|
+
}'
|
|
257
|
+
|
|
258
|
+
# 2. Use WEBHOOK action in a rule
|
|
259
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
260
|
+
"name": "Large Order Alert",
|
|
261
|
+
"priority": 0,
|
|
262
|
+
"condition": {
|
|
263
|
+
"type": "SINGLE",
|
|
264
|
+
"field": "order_total",
|
|
265
|
+
"operator": "GREATER_THAN",
|
|
266
|
+
"value": 1000000,
|
|
267
|
+
"valueType": "NUMBER"
|
|
268
|
+
},
|
|
269
|
+
"actions": [
|
|
270
|
+
{ "type": "WEBHOOK", "parameters": { "url": "https://api.example.com/webhooks/orders", "method": "POST" } },
|
|
271
|
+
{ "type": "ADD_TAG", "parameters": { "tag": "large_order" } }
|
|
272
|
+
]
|
|
273
|
+
}'
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Recipe 6: Exclusive Discount (Mutex)
|
|
279
|
+
|
|
280
|
+
**Goal:** Ensure only the best discount applies when multiple rules match.
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
284
|
+
"name": "VIP Discount 20%",
|
|
285
|
+
"priority": 0,
|
|
286
|
+
"mutexGroup": "best-discount",
|
|
287
|
+
"mutexMode": "EXCLUSIVE",
|
|
288
|
+
"mutexStrategy": "HIGHEST_PRIORITY",
|
|
289
|
+
"condition": {
|
|
290
|
+
"type": "SINGLE", "field": "customer_tier", "operator": "EQUALS", "value": "VIP", "valueType": "STRING"
|
|
291
|
+
},
|
|
292
|
+
"actions": [{ "type": "DISCOUNT", "parameters": { "method": "PERCENTAGE", "rate": 20, "referenceFactKey": "payment_amount" } }]
|
|
293
|
+
}'
|
|
294
|
+
|
|
295
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
296
|
+
"name": "Seasonal Sale 15%",
|
|
297
|
+
"priority": 1,
|
|
298
|
+
"mutexGroup": "best-discount",
|
|
299
|
+
"mutexMode": "EXCLUSIVE",
|
|
300
|
+
"mutexStrategy": "HIGHEST_PRIORITY",
|
|
301
|
+
"condition": {
|
|
302
|
+
"type": "SINGLE", "field": "payment_amount", "operator": "GREATER_THAN_OR_EQUAL", "value": 50000, "valueType": "NUMBER"
|
|
303
|
+
},
|
|
304
|
+
"actions": [{ "type": "DISCOUNT", "parameters": { "method": "PERCENTAGE", "rate": 15, "referenceFactKey": "payment_amount" } }]
|
|
305
|
+
}'
|
|
306
|
+
|
|
307
|
+
# If a VIP customer pays 50000+, only the 20% VIP discount fires (priority 0 wins).
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Recipe 7: Region-Based Coupon
|
|
313
|
+
|
|
314
|
+
**Goal:** Issue different coupons by region.
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
lexq facts create --key user_region --name "User Region" --type STRING --required
|
|
318
|
+
|
|
319
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
320
|
+
"name": "Korea Welcome Coupon",
|
|
321
|
+
"priority": 0,
|
|
322
|
+
"condition": {
|
|
323
|
+
"type": "SINGLE", "field": "user_region", "operator": "IN", "value": ["KR"], "valueType": "LIST_STRING"
|
|
324
|
+
},
|
|
325
|
+
"actions": [{ "type": "COUPON_ISSUE", "parameters": { "couponId": "KR_WELCOME_2025", "expiryDays": 30 } }]
|
|
326
|
+
}'
|
|
327
|
+
|
|
328
|
+
lexq rules create --group-id <gid> --version-id <vid> --json '{
|
|
329
|
+
"name": "US Welcome Coupon",
|
|
330
|
+
"priority": 1,
|
|
331
|
+
"condition": {
|
|
332
|
+
"type": "SINGLE", "field": "user_region", "operator": "IN", "value": ["US"], "valueType": "LIST_STRING"
|
|
333
|
+
},
|
|
334
|
+
"actions": [{ "type": "COUPON_ISSUE", "parameters": { "couponId": "US_WELCOME_2025", "expiryDays": 14 } }]
|
|
335
|
+
}'
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Recipe 8: Version Rollback
|
|
341
|
+
|
|
342
|
+
**Goal:** Something went wrong in production — revert to the previous version.
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
# 1. Check current state
|
|
346
|
+
lexq deploy overview
|
|
347
|
+
|
|
348
|
+
# 2. Rollback
|
|
349
|
+
lexq deploy rollback --group-id <gid> --memo "Reverting due to increased error rate"
|
|
350
|
+
|
|
351
|
+
# 3. Verify
|
|
352
|
+
lexq deploy overview
|
|
353
|
+
lexq history stats
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Recipe 9: Monitoring + Auto-Resolve Failures
|
|
359
|
+
|
|
360
|
+
**Goal:** Check for pending failures and resolve them.
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
# 1. List pending failures
|
|
364
|
+
lexq logs list --status PENDING --page 0 --size 50
|
|
365
|
+
|
|
366
|
+
# 2. Retry transient failures
|
|
367
|
+
lexq logs bulk-action --ids "id1,id2,id3" --action RETRY
|
|
368
|
+
|
|
369
|
+
# 3. Resolve permanent failures (after manual review)
|
|
370
|
+
lexq logs bulk-action --ids "id4,id5" --action RESOLVE
|
|
371
|
+
|
|
372
|
+
# 4. Verify clean state
|
|
373
|
+
lexq logs list --status PENDING --page 0 --size 10
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Recipe 10: Full Policy Migration Workflow
|
|
379
|
+
|
|
380
|
+
**Goal:** Create an entirely new version of a policy with different logic.
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
# 1. Clone the current live version
|
|
384
|
+
lexq versions clone --group-id <gid> --version-id <currentLiveVersionId>
|
|
385
|
+
# → new DRAFT version ID
|
|
386
|
+
|
|
387
|
+
# 2. Delete unwanted rules from the clone
|
|
388
|
+
lexq rules list --group-id <gid> --version-id <newVid>
|
|
389
|
+
lexq rules delete --group-id <gid> --version-id <newVid> --id <obsoleteRuleId> --force
|
|
390
|
+
|
|
391
|
+
# 3. Add new rules
|
|
392
|
+
lexq rules create --group-id <gid> --version-id <newVid> --json '{...}'
|
|
393
|
+
|
|
394
|
+
# 4. Dry-run test multiple scenarios
|
|
395
|
+
lexq analytics dry-run --version-id <newVid> --debug --mock --json '{"facts":{...}}'
|
|
396
|
+
|
|
397
|
+
# 5. Run simulation against live baseline
|
|
398
|
+
lexq deploy publish --group-id <gid> --version-id <newVid> --memo "v2 migration"
|
|
399
|
+
lexq analytics simulation start --json '{
|
|
400
|
+
"policyVersionId": "<newVid>",
|
|
401
|
+
"dataset": {"type":"HISTORICAL","source":"EXECUTION_LOGS","from":"2025-01-01","to":"2025-01-31"},
|
|
402
|
+
"options": {"baselinePolicyVersionId":"<currentLiveVersionId>","includeRuleStats":true}
|
|
403
|
+
}'
|
|
404
|
+
|
|
405
|
+
# 6. If simulation passes, deploy
|
|
406
|
+
lexq deploy live --group-id <gid> --version-id <newVid> --memo "Migration complete"
|
|
407
|
+
```
|