@lexq/cli 0.1.51 → 0.1.53
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/dist/index.js
CHANGED
|
@@ -3405,7 +3405,7 @@ function registerGroupTools(server, callApi) {
|
|
|
3405
3405
|
"lexq_ab_test_start",
|
|
3406
3406
|
{
|
|
3407
3407
|
title: "Start A/B Test",
|
|
3408
|
-
description: "Start an A/B test on a policy group. Requires a challenger version ID and traffic rate.",
|
|
3408
|
+
description: "Start an A/B test on a policy group. Requires a challenger version ID and traffic rate. The split is computed from context.trafficKey on each execution request; requests that omit it never reach the challenger and the test stays at 0%.",
|
|
3409
3409
|
inputSchema: {
|
|
3410
3410
|
groupId: z.string().uuid().describe("Policy group ID"),
|
|
3411
3411
|
testVersionId: z.string().uuid().describe("Challenger version ID to test"),
|
package/dist/mcp/register.js
CHANGED
|
@@ -128,7 +128,7 @@ function registerGroupTools(server, callApi) {
|
|
|
128
128
|
"lexq_ab_test_start",
|
|
129
129
|
{
|
|
130
130
|
title: "Start A/B Test",
|
|
131
|
-
description: "Start an A/B test on a policy group. Requires a challenger version ID and traffic rate.",
|
|
131
|
+
description: "Start an A/B test on a policy group. Requires a challenger version ID and traffic rate. The split is computed from context.trafficKey on each execution request; requests that omit it never reach the challenger and the test stays at 0%.",
|
|
132
132
|
inputSchema: {
|
|
133
133
|
groupId: z.string().uuid().describe("Policy group ID"),
|
|
134
134
|
testVersionId: z.string().uuid().describe("Challenger version ID to test"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexq/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"description": "LexQ CLI — manage policies, simulate rules, and deploy from the terminal. Built for humans and AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"enums": "node scripts/gen-enums.mjs",
|
|
31
31
|
"enums:check": "node scripts/gen-enums.mjs --check",
|
|
32
32
|
"constants:check": "node scripts/check-constants.mjs",
|
|
33
|
+
"surface:check": "node scripts/check-public-surface.mjs",
|
|
34
|
+
"ab-key:check": "node scripts/check-ab-key.mjs",
|
|
33
35
|
"start": "node dist/index.js",
|
|
34
36
|
"test:decimals": "pnpm build && node tests/exact-decimals.mjs",
|
|
35
37
|
"test:fact-key": "node tests/fact-key.mjs",
|
|
@@ -159,6 +159,33 @@ lexq groups create --json '{
|
|
|
159
159
|
|
|
160
160
|
Split traffic between the current live version and a test version.
|
|
161
161
|
|
|
162
|
+
### Traffic Identity Key
|
|
163
|
+
|
|
164
|
+
**Every execution request has to carry `context.trafficKey`.** That value is what the split is
|
|
165
|
+
computed from: the server hashes it together with the group ID and assigns the request to a
|
|
166
|
+
bucket, so the same key value always reaches the same version for that group.
|
|
167
|
+
|
|
168
|
+
Send it in `context`, not in `facts`. Routing never reads `facts`.
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"facts": { "paymentAmount": 150000, "customerTier": "VIP" },
|
|
173
|
+
"context": { "trafficKey": "customer-8f3a21" }
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
If the key is missing or empty, no request is ever assigned to the test version. The test still
|
|
178
|
+
reports as running and execution logs keep accumulating, so **the failure does not surface in the
|
|
179
|
+
response** — only a server-side warning records it. This is the most common reason a test sits
|
|
180
|
+
at 0%.
|
|
181
|
+
|
|
182
|
+
Pick a value that is stable per experiment unit (a customer ID, a merchant ID, a session ID) and
|
|
183
|
+
opaque — the whole `context` object is stored on the execution ledger, so send a hash or a
|
|
184
|
+
surrogate key rather than an email address. A value that changes every request, such as a request
|
|
185
|
+
ID, scatters one user across both versions and destroys the comparison.
|
|
186
|
+
|
|
187
|
+
Batch calls read `sharedContext.trafficKey` instead, and the whole batch runs on one version.
|
|
188
|
+
|
|
162
189
|
### Start A/B Test
|
|
163
190
|
|
|
164
191
|
```bash
|
|
@@ -191,11 +218,12 @@ This reverts all traffic to the main version. The test version remains ACTIVE bu
|
|
|
191
218
|
```
|
|
192
219
|
1. Create two versions (v1 live, v2 DRAFT with changes)
|
|
193
220
|
2. Publish v2: lexq deploy publish --group-id <gid> --version-id <v2id>
|
|
194
|
-
3.
|
|
195
|
-
4.
|
|
196
|
-
5.
|
|
197
|
-
6.
|
|
198
|
-
7.
|
|
221
|
+
3. Confirm your execution requests send context.trafficKey
|
|
222
|
+
4. Start A/B: lexq groups ab-test start --group-id <gid> --version-id <v2id> --traffic-rate 10
|
|
223
|
+
5. Monitor: lexq history stats (compare metrics)
|
|
224
|
+
6. Adjust traffic gradually: 10% → 30% → 50%
|
|
225
|
+
7. Promote winner: lexq deploy live --group-id <gid> --version-id <v2id>
|
|
226
|
+
8. Stop test: lexq groups ab-test stop --group-id <gid> --force
|
|
199
227
|
```
|
|
200
228
|
|
|
201
229
|
## Common Patterns
|
|
@@ -190,6 +190,7 @@ lexq analytics dry-run --version-id <v2id> --debug --json '{
|
|
|
190
190
|
lexq deploy publish --group-id <gid> --version-id <v2id> --memo "15% discount test"
|
|
191
191
|
|
|
192
192
|
# 5. Start A/B test at 10% traffic
|
|
193
|
+
# Your execution requests must carry context.trafficKey, or the test stays at 0%.
|
|
193
194
|
lexq groups ab-test start --group-id <gid> --version-id <v2id> --traffic-rate 10
|
|
194
195
|
|
|
195
196
|
# 6. Monitor (check execution stats periodically)
|
|
@@ -60,6 +60,11 @@ Response includes:
|
|
|
60
60
|
|
|
61
61
|
**Always run this before dry-run.** Copy `exampleRequest.facts` as your starting template and fill in real values.
|
|
62
62
|
|
|
63
|
+
`exampleRequest.context` comes back empty, and it stays empty even when the group has an A/B test
|
|
64
|
+
running. Live execution requests against such a group also need `context.trafficKey` or the test
|
|
65
|
+
version receives no traffic — see the A/B testing section of the groups skill. Dry-run itself is
|
|
66
|
+
unaffected: it always evaluates the version you name, so routing never applies here.
|
|
67
|
+
|
|
63
68
|
## 2. Dry Run (Single Input)
|
|
64
69
|
|
|
65
70
|
Test a single set of facts against a version:
|