@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.
@@ -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
+ ```