@lexq/cli 0.1.40 → 0.1.42

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.
@@ -5,7 +5,7 @@
5
5
  ## Overview
6
6
 
7
7
  Once policies are deployed, the engine evaluates incoming facts against live rules. This skill covers **observing**
8
- production behavior: execution history, statistics, failure logs, and integrations.
8
+ production behavior: execution history, statistics, failure logs, and deployment state.
9
9
 
10
10
  ## 1. Execution History
11
11
 
@@ -28,12 +28,16 @@ Filter options:
28
28
 
29
29
  ### Execution Statuses
30
30
 
31
- | Status | Meaning |
32
- |------------|------------------------------------------------|
33
- | `SUCCESS` | At least one rule matched and actions executed |
34
- | `NO_MATCH` | No rules matched the input facts |
35
- | `ERROR` | Engine error during evaluation |
36
- | `TIMEOUT` | Execution exceeded time limit |
31
+ | Status | Meaning |
32
+ |------------|-----------------------------------------------------------------------------------------------|
33
+ | `SUCCESS` | At least one rule matched and actions executed |
34
+ | `NO_MATCH` | No rules matched — or the version was not evaluated at all (e.g. outside its effective dates) |
35
+ | `ERROR` | Engine error during evaluation |
36
+ | `TIMEOUT` | Execution exceeded time limit |
37
+
38
+ A version outside its effective date range is filtered before evaluation, so no per-rule trace is produced for it. If a
39
+ rule you expect never appears in `decisionTraces`, check the version's `effectiveFrom` / `effectiveTo` with
40
+ `lexq versions get`.
37
41
 
38
42
  ### Get Execution Detail
39
43
 
@@ -70,7 +74,16 @@ Use stats to monitor:
70
74
 
71
75
  ## 2. Failure Logs
72
76
 
73
- System failure logs capture errors from background tasks (batch jobs, webhook calls, scheduled operations).
77
+ Failure logs are a dead-letter record for background tasks that failed after the request returned.
78
+ Two task types exist:
79
+
80
+ | Task type | What failed |
81
+ |------------------------|------------------------------------------------------------------|
82
+ | `PLATFORM_WEBHOOK` | A platform event webhook delivery (`lexq webhook-subscriptions`) |
83
+ | `SCHEDULED_DEPLOYMENT` | A scheduled deployment that could not be armed or executed |
84
+
85
+ Policy execution failures do **not** land here — they surface in execution history with
86
+ status `ERROR`.
74
87
 
75
88
  ### List Failure Logs
76
89
 
@@ -80,14 +93,13 @@ lexq logs list --page 0 --size 20
80
93
 
81
94
  Filter options:
82
95
 
83
- | Flag | Description | Values |
84
- |-----------------------|-----------------------------------------|----------------------------------------------------------------------|
85
- | `--category <cat>` | Task category | `INTEGRATION`, `INTERNAL` |
86
- | `--task-type <type>` | Task type | `COUPON_ISSUE`, `POINT_EARN`, `NOTIFICATION_SEND`, `WEBHOOK_EXECUTE` |
87
- | `--status <status>` | Log status | `PENDING`, `RESOLVED`, `IGNORED` |
88
- | `--keyword <kw>` | Search in refId, refSubId, errorMessage | any string |
89
- | `--start-date <date>` | Start date (ISO) | `2025-01-01` |
90
- | `--end-date <date>` | End date (ISO) | `2025-01-31` |
96
+ | Flag | Description | Values |
97
+ |-----------------------|-----------------------------------------|--------------------------------------------|
98
+ | `--task-type <type>` | Task type | `PLATFORM_WEBHOOK`, `SCHEDULED_DEPLOYMENT` |
99
+ | `--status <status>` | Log status | `PENDING`, `RESOLVED`, `IGNORED` |
100
+ | `--keyword <kw>` | Search in refId, refSubId, errorMessage | any string |
101
+ | `--start-date <date>` | Start date (ISO) | `2025-01-01` |
102
+ | `--end-date <date>` | End date (ISO) | `2025-01-31` |
91
103
 
92
104
  ### Get Failure Log Detail
93
105
 
@@ -95,102 +107,142 @@ Filter options:
95
107
  lexq logs get --id <logId>
96
108
  ```
97
109
 
98
- ### Retry Failed Log
110
+ Includes the original payload and error message. Use it to diagnose before marking the log.
99
111
 
100
- ```bash
101
- lexq logs action --id <logId> --action RETRY
102
- ```
112
+ ### Mark a Log
103
113
 
104
- ### Resolve (Mark as Handled)
114
+ There is no retry action — the engine does not re-execute a failed task on request. Fix the cause
115
+ at the source (webhook endpoint, version state), then mark the log.
105
116
 
106
117
  ```bash
107
- lexq logs action --id <logId> --action RESOLVE
118
+ lexq logs action --id <logId> --action RESOLVE # cause was fixed
119
+ lexq logs action --id <logId> --action IGNORE # does not need fixing
108
120
  ```
109
121
 
110
- ### Ignore (Skip Retries)
111
-
112
- ```bash
113
- lexq logs action --id <logId> --action IGNORE
114
- ```
122
+ Both remove the log from `PENDING`; they differ only in recorded intent.
115
123
 
116
124
  ### Bulk Operations
117
125
 
118
126
  ```bash
119
- lexq logs bulk-action --ids "id1,id2,id3" --action RETRY
120
127
  lexq logs bulk-action --ids "id1,id2,id3" --action RESOLVE
128
+ lexq logs bulk-action --ids "id4,id5" --action IGNORE
121
129
  ```
122
130
 
123
- ## 3. Integrations
131
+ ## 3. Deployment Monitoring
124
132
 
125
- Integrations connect LexQ actions to external systems (webhooks, CRM, notification services).
133
+ ### Deployment Overview
126
134
 
127
- ### List Integrations
135
+ See all groups' deployment status at a glance:
128
136
 
129
137
  ```bash
130
- lexq integrations list --page 0 --size 10
138
+ lexq deploy overview
131
139
  ```
132
140
 
133
- ### Get Integration
141
+ Returns an array with each group's current version, last deployment type, and deployer.
142
+
143
+ ### Deployment History
134
144
 
135
145
  ```bash
136
- lexq integrations get --id <integrationId>
146
+ lexq deploy history --page 0 --size 20
147
+ lexq deploy history --group-id <gid>
148
+ lexq deploy history --types DEPLOY,ROLLBACK --start-date 2025-01-01
137
149
  ```
138
150
 
139
- ### Get Config Spec
140
-
141
- Shows available integration types and their required configuration fields:
151
+ ### Deployment Detail
142
152
 
143
153
  ```bash
144
- lexq integrations config-spec
154
+ lexq deploy detail --id <deploymentId>
145
155
  ```
146
156
 
147
- ### Save (Create or Update)
157
+ Includes snapshot hash verification (`hashValid` field) to detect if a version was modified after deployment.
158
+
159
+ ### Scheduled Deployment
160
+
161
+ A version with a future `effectiveFrom` can be armed to deploy itself at that time.
148
162
 
149
163
  ```bash
150
- lexq integrations save --json '{
151
- "type": "WEBHOOK",
152
- "name": "Order Webhook",
153
- "baseUrl": "https://api.example.com/webhook",
154
- "isActive": true
155
- }'
164
+ # Arm the version must be ACTIVE (published) with a future effective start
165
+ lexq deploy schedule --group-id <gid> --version-id <vid> --memo "Q4 pricing"
166
+
167
+ # List all schedules (PENDING, EXECUTED, CANCELED, FAILED)
168
+ lexq deploy schedules --format table
169
+
170
+ # Cancel the pending schedule (the version itself is untouched)
171
+ lexq deploy unschedule --group-id <gid> --force
156
172
  ```
157
173
 
158
- Integration types: `COUPON`, `POINT`, `NOTIFICATION`, `CRM`, `MESSENGER`, `WEBHOOK`
174
+ One pending schedule per group. The scheduler deploys within one tick (≤60s) of the effective
175
+ start. A manual deploy, rollback, undeploy, A/B test start, or group archive **cancels** the
176
+ pending schedule — the cancellation reason is recorded in the schedule ledger.
177
+
178
+ The snapshot hash is sealed at scheduling time. If the version changes before the effective start,
179
+ the scheduled deploy fails with `HASH_MISMATCH` rather than deploying something unexpected.
159
180
 
160
- ### Delete Integration
181
+ ## 4. Decision Provenance
182
+
183
+ Every execution is sealed with a `traceId`. Provenance answers "why did this decision happen"
184
+ deterministically — which rules fired, which lost, and to what.
161
185
 
162
186
  ```bash
163
- lexq integrations delete --id <integrationId>
164
- lexq integrations delete --id <integrationId> --force
187
+ lexq provenance get --trace-id <traceId>
165
188
  ```
166
189
 
167
- ## 4. Deployment Monitoring
190
+ Returns the decision, per-rule reason codes (`FINAL_WINNER`, `CONDITION_MISMATCH`, `MUTEX_PRIORITY_LOST`,
191
+ `GROUP_LIMIT_REACHED`, …), and the sealed version snapshot hash.
168
192
 
169
- ### Deployment Overview
193
+ A rule that lost conflict resolution has status `BLOCKED` — unrelated to the `BLOCK` action, which writes the
194
+ `is_blocked` fact while its own rule stays `SELECTED`.
170
195
 
171
- See all groups' deployment status at a glance:
196
+ ### PII Reveal Audit
197
+
198
+ Facts marked `isPii` are masked on every read surface. Unmasking is possible only in the console
199
+ and every reveal is recorded.
172
200
 
173
201
  ```bash
174
- lexq deploy overview
202
+ lexq provenance reveal-audits
175
203
  ```
176
204
 
177
- Returns an array with each group's current version, last deployment type, and deployer.
205
+ Returns who revealed which fact of which trace, and when.
178
206
 
179
- ### Deployment History
207
+ ## 5. Decision Replay
208
+
209
+ Re-evaluate past executions against a candidate version to see what would have changed.
180
210
 
181
211
  ```bash
182
- lexq deploy history --page 0 --size 20
183
- lexq deploy history --group-id <gid>
184
- lexq deploy history --types DEPLOY,ROLLBACK --start-date 2025-01-01
212
+ # Single decision
213
+ lexq replay decision --trace-id <traceId> --version-id <candidateVid>
214
+
215
+ # A date window, as an async job
216
+ lexq replay start --version-id <candidateVid> --from 2025-01-01 --to 2025-01-31
217
+ # --max-records caps the sample (hard cap 50k)
218
+ lexq replay get --id <jobId>
219
+ lexq replay list
220
+ lexq replay cancel --id <jobId>
185
221
  ```
186
222
 
187
- ### Deployment Detail
223
+ Replay reports whether the outcome is `DETERMINISTIC` or `REPLAY_MAY_DIFFER` — the latter when the
224
+ original execution depended on values the replay cannot reproduce.
225
+
226
+ ## 6. Latency Profile
227
+
228
+ Per-rule latency distribution for a policy group.
188
229
 
189
230
  ```bash
190
- lexq deploy detail --id <deploymentId>
231
+ lexq profile <groupId>
232
+
233
+ # Single-rule detail — distributions + 60s window series
234
+ lexq profile <groupId> --rule <ruleId>
235
+
236
+ # Defaults: live version, last 24h, cache HIT
237
+ lexq profile <groupId> --version <vid> --cache MISS \
238
+ --from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z
191
239
  ```
192
240
 
193
- Includes snapshot hash verification (`hashValid` field) to detect if a version was modified after deployment.
241
+ `--from` / `--to` are ISO-8601 **instants** here (`2026-07-01T00:00:00Z`), unlike `replay start`
242
+ which takes `yyyy-MM-dd` dates.
243
+
244
+ `p50` / `p95` / `p99` are `null` when the sample is under 100 — the engine reports insufficient
245
+ data rather than inventing a number. `n` is always present.
194
246
 
195
247
  ## Agent Monitoring Workflow
196
248
 
@@ -201,13 +253,16 @@ lexq history stats
201
253
  # 2. If failure rate is high, investigate
202
254
  lexq history list --status ERROR --page 0 --size 10
203
255
 
204
- # 3. Check for system failures
256
+ # 3. For a specific bad decision, get the deterministic why
257
+ lexq provenance get --trace-id <traceId>
258
+
259
+ # 4. Check for system failures
205
260
  lexq logs list --status PENDING --page 0 --size 10
206
261
 
207
- # 4. Retry or resolve failures
208
- lexq logs action --id <logId> --action RETRY
262
+ # 5. Inspect and mark
263
+ lexq logs get --id <logId>
209
264
  lexq logs action --id <logId> --action RESOLVE
210
265
 
211
- # 5. Verify deployment integrity
266
+ # 6. Verify deployment integrity
212
267
  lexq deploy overview
213
268
  ```
@@ -4,19 +4,21 @@
4
4
 
5
5
  ## Overview
6
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.
7
+ A **Policy Group** is the top-level container. It holds versions (each containing rules), manages deployment state, and
8
+ controls conflict resolution when multiple groups interact.
8
9
 
9
10
  ## Status Lifecycle
10
11
 
11
- ```
12
- ACTIVE ──→ DISABLED (emergency stop, re-enable anytime)
13
- │ │
14
- └──→ ARCHIVED ←┘ (permanent, cannot be restored)
15
- ```
12
+ | From | To | Reversible |
13
+ |------------|------------|-----------------------------------|
14
+ | `ACTIVE` | `DISABLED` | yes — set status back to `ACTIVE` |
15
+ | `DISABLED` | `ACTIVE` | — |
16
+ | `ACTIVE` | `ARCHIVED` | **no** |
17
+ | `DISABLED` | `ARCHIVED` | **no** |
16
18
 
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.
19
+ - **ACTIVE** executions are processed.
20
+ - **DISABLED** all executions are blocked. Use for emergency stop.
21
+ - **ARCHIVED** removed from execution. Reached only through `lexq groups delete`, not by setting `status` directly.
20
22
 
21
23
  ## CRUD Commands
22
24
 
@@ -40,36 +42,49 @@ Returns full detail including `activationMode`, `activationStrategy`, `execution
40
42
  lexq groups create --json '{
41
43
  "name": "discount-policy",
42
44
  "description": "VIP discount rules",
43
- "priority": 0,
44
45
  "activationMode": "NONE",
45
- "activationStrategy": "FIRST_MATCH",
46
+ "activationStrategy": "HIGHEST_PRIORITY",
46
47
  "status": "ACTIVE"
47
48
  }'
48
49
  ```
49
50
 
50
- **Required fields:** `name`, `priority`
51
+ **Required field:** `name`
52
+
53
+ `priority` is assigned by the server, not by you — new groups are appended last. Change the order
54
+ with `lexq groups reorder`. Priorities are 1-based (1 is highest).
51
55
 
52
56
  **Optional fields with defaults:**
53
57
 
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 |
58
+ | Field | Default | Description |
59
+ |----------------------|--------------------|----------------------------------------------------|
60
+ | `activationMode` | `NONE` | Conflict resolution mode |
61
+ | `activationStrategy` | `HIGHEST_PRIORITY` | Strategy within the mode. Currently the only value |
62
+ | `executionLimit` | `null` | Max rules that can fire |
63
+ | `activationGroup` | `null` | Logical group for cross-group conflict resolution |
64
+ | `status` | `ACTIVE` | Initial status |
61
65
 
62
66
  ### Update Group
63
67
 
64
68
  ```bash
65
69
  lexq groups update --id <groupId> --json '{
66
70
  "name": "updated-discount-policy",
67
- "priority": 1
71
+ "description": "Updated description"
68
72
  }'
69
73
  ```
70
74
 
71
75
  Only include fields you want to change. Omitted fields are not modified.
72
76
 
77
+ ### Reorder Groups
78
+
79
+ Priorities are **tenant-wide**, not per activation group. Pass group IDs in desired order; the
80
+ server assigns 1, 2, 3, … (1…N continuous):
81
+
82
+ ```bash
83
+ lexq groups reorder --group-ids "<gid_A>,<gid_B>,<gid_C>"
84
+ ```
85
+
86
+ `activationGroup` is not affected — reorder changes priority only.
87
+
73
88
  ### Delete Group
74
89
 
75
90
  ```bash
@@ -78,27 +93,45 @@ lexq groups delete --id <groupId>
78
93
  lexq groups delete --id <groupId> --force
79
94
  ```
80
95
 
81
- **Warning:** Deleting a group cascades all versions, rules, and deployment history are removed.
96
+ **`delete` archives it does not erase.** The group becomes `ARCHIVED` and stops executing;
97
+ versions, rules, and history are retained for audit. Deleting an already-archived group is a
98
+ no-op.
99
+
100
+ Two conditions block it:
101
+
102
+ | Condition | What to do first |
103
+ |------------------------|-----------------------------------------------------|
104
+ | The group is deployed | `lexq deploy undeploy --group-id <gid>` |
105
+ | An A/B test is running | `lexq groups ab-test stop --group-id <gid> --force` |
106
+
107
+ Archiving cancels any pending scheduled deployment and closes the priority gap — remaining groups
108
+ are renumbered 1…N.
82
109
 
83
110
  ## Conflict Resolution
84
111
 
85
112
  ### Activation Mode (across groups in the same `activationGroup`)
86
113
 
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. |
114
+ | Mode | Behavior |
115
+ |-------------|--------------------------------------------------------------------|
116
+ | `NONE` | All matching rules fire. No conflict resolution. |
117
+ | `EXCLUSIVE` | Only one group wins within the activation group. |
118
+ | `MAX_N` | Up to `executionLimit` groups fire. Omit it and the server sets 2. |
92
119
 
93
120
  ### Activation Strategy
94
121
 
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. |
122
+ | Strategy | Behavior |
123
+ |--------------------|--------------------------------------------------------------------------|
124
+ | `HIGHEST_PRIORITY` | Lowest priority number wins (1 is highest). Currently the only strategy. |
100
125
 
101
- **Constraint:** All groups sharing the same `activationGroup` **must** use identical `activationMode` and `activationStrategy`. The API rejects mismatches with `ACTIVATION_CONFIG_MISMATCH`.
126
+ **Constraint:** All groups sharing the same `activationGroup` **must** use identical
127
+ `activationMode` and `activationStrategy`. The API rejects mismatches with a `P-` error.
128
+
129
+ Groups are selected by the order you set, not by comparing action values. `FIRST_MATCH` and
130
+ `MAX_BENEFIT` were removed on 2026-08-01 — the former resolved identically to
131
+ `HIGHEST_PRIORITY` (candidates are evaluated in priority order, so "first match" *is* "lowest
132
+ priority number"), and the latter compared post-action values as scores, which made a 20%
133
+ discount lose to a 10% discount. The engine cannot know which direction is "better" for your
134
+ domain.
102
135
 
103
136
  ### Example: Exclusive Discount Groups
104
137
 
@@ -106,7 +139,6 @@ lexq groups delete --id <groupId> --force
106
139
  # Only one of these can fire per execution
107
140
  lexq groups create --json '{
108
141
  "name": "vip-discount",
109
- "priority": 0,
110
142
  "activationGroup": "discounts",
111
143
  "activationMode": "EXCLUSIVE",
112
144
  "activationStrategy": "HIGHEST_PRIORITY"
@@ -114,11 +146,13 @@ lexq groups create --json '{
114
146
 
115
147
  lexq groups create --json '{
116
148
  "name": "seasonal-discount",
117
- "priority": 1,
118
149
  "activationGroup": "discounts",
119
150
  "activationMode": "EXCLUSIVE",
120
151
  "activationStrategy": "HIGHEST_PRIORITY"
121
152
  }'
153
+
154
+ # Groups are appended in creation order. Set it explicitly if it matters:
155
+ # lexq groups reorder --group-ids "<vipGid>,<seasonalGid>"
122
156
  ```
123
157
 
124
158
  ## A/B Testing
@@ -128,11 +162,14 @@ Split traffic between the current live version and a test version.
128
162
  ### Start A/B Test
129
163
 
130
164
  ```bash
131
- lexq groups ab-test start --group-id <groupId> --version-id <publishedVersionId> --traffic-rate 30
165
+ lexq groups ab-test start --group-id <groupId> --version-id <activeVersionId> --traffic-rate 30
132
166
  ```
133
167
 
134
168
  `--traffic-rate` is the percentage (1–99) of traffic routed to the test version.
135
169
 
170
+ The test version must be `ACTIVE` (published) and different from the live version. Starting a test cancels any pending
171
+ scheduled deployment for the group.
172
+
136
173
  ### Adjust Traffic
137
174
 
138
175
  ```bash
@@ -156,7 +193,7 @@ This reverts all traffic to the main version. The test version remains ACTIVE bu
156
193
  2. Publish v2: lexq deploy publish --group-id <gid> --version-id <v2id>
157
194
  3. Start A/B: lexq groups ab-test start --group-id <gid> --version-id <v2id> --traffic-rate 10
158
195
  4. Monitor: lexq history stats (compare metrics)
159
- 5. Adjust traffic gradually: 10% → 30% → 50% → 100%
196
+ 5. Adjust traffic gradually: 10% → 30% → 50%
160
197
  6. Promote winner: lexq deploy live --group-id <gid> --version-id <v2id>
161
198
  7. Stop test: lexq groups ab-test stop --group-id <gid> --force
162
199
  ```
@@ -168,10 +205,13 @@ This reverts all traffic to the main version. The test version remains ACTIVE bu
168
205
  Always list existing groups first to avoid duplicates:
169
206
 
170
207
  ```bash
171
- lexq groups list --format json | # parse and check if name exists
172
- lexq groups create --json '...'
208
+ lexq groups list --size 100
209
+ lexq groups create --json '{"name": "discount-policy"}'
173
210
  ```
174
211
 
212
+ Group names are unique per tenant — a duplicate is rejected, not silently created. Listing first
213
+ lets you reuse the existing group instead of handling the error.
214
+
175
215
  ### Emergency Stop
176
216
 
177
217
  ```bash