@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"),
@@ -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.51",
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. Start A/B: lexq groups ab-test start --group-id <gid> --version-id <v2id> --traffic-rate 10
195
- 4. Monitor: lexq history stats (compare metrics)
196
- 5. Adjust traffic gradually: 10% 30% → 50%
197
- 6. Promote winner: lexq deploy live --group-id <gid> --version-id <v2id>
198
- 7. Stop test: lexq groups ab-test stop --group-id <gid> --force
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: