@drip-apex/apex-cli 0.11.0 → 0.12.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/README.md +12 -0
- package/dist/cli.js +63 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -342,3 +342,15 @@ optimistic concurrency token.
|
|
|
342
342
|
QA status creates a run only when none exists. Reading an existing run never writes
|
|
343
343
|
to it: `updatedAt` is its last stored mutation time, while `computedAt` is the ISO
|
|
344
344
|
timestamp when the current checklist and summary were computed.
|
|
345
|
+
|
|
346
|
+
### Decision latency
|
|
347
|
+
|
|
348
|
+
`apex tracking latency <experiment-id-or-key> [--days <1..30>] [--qa-session <id>] [--json]`
|
|
349
|
+
compares forced-preview and natural decision latency before launch. The default
|
|
350
|
+
window is seven days. The table shows sample counts, decision p50/p95/p99,
|
|
351
|
+
config-fetch p50/p95 (milliseconds), and inline/cache/network/none counts.
|
|
352
|
+
JSON output includes the API body in the standard envelope and assignment-ready
|
|
353
|
+
percentiles. Fewer than five measured samples have null percentiles. Natural
|
|
354
|
+
loads are shop-wide non-QA loads without forced IDs; a QA-session bucket can
|
|
355
|
+
overlap forced loads. Analytics failures return errors. Preload and anti-flicker
|
|
356
|
+
changes remain under evaluation pending the first report.
|
package/dist/cli.js
CHANGED
|
@@ -7112,7 +7112,7 @@ var import_node_os3 = __toESM(require("node:os"));
|
|
|
7112
7112
|
// package.json
|
|
7113
7113
|
var package_default = {
|
|
7114
7114
|
name: "@drip-apex/apex-cli",
|
|
7115
|
-
version: "0.
|
|
7115
|
+
version: "0.12.0",
|
|
7116
7116
|
license: "MIT",
|
|
7117
7117
|
description: "Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.",
|
|
7118
7118
|
private: false,
|
|
@@ -7985,6 +7985,11 @@ function trackingEventProperties(event) {
|
|
|
7985
7985
|
if (event.goal_id != null) properties.goalId = event.goal_id;
|
|
7986
7986
|
return properties;
|
|
7987
7987
|
}
|
|
7988
|
+
function getDecisionLatency(creds, experimentId, opts) {
|
|
7989
|
+
const params = new URLSearchParams({ experiment_id: experimentId, days: String(opts.days) });
|
|
7990
|
+
if (opts.qaSessionId) params.set("qa_session_id", opts.qaSessionId);
|
|
7991
|
+
return request(creds, "GET", `/api/v1/tracking/latency?${params}`);
|
|
7992
|
+
}
|
|
7988
7993
|
function verifyTracking(creds, opts) {
|
|
7989
7994
|
const params = new URLSearchParams();
|
|
7990
7995
|
if (opts?.minutes) params.set("minutes", String(opts.minutes));
|
|
@@ -13096,6 +13101,63 @@ trackingCommand.command("verify").description("Verify live tracking events for t
|
|
|
13096
13101
|
}
|
|
13097
13102
|
});
|
|
13098
13103
|
});
|
|
13104
|
+
trackingCommand.command("latency").description("Measure forced-preview versus natural decision latency before launch").argument("<experiment-id-or-key>", "Experiment ID or test key").option("--days <n>", "Lookback window in days (1\u201330)", "7").option("--qa-session <id>", "Include a QA session bucket").action(async function(experimentId, opts) {
|
|
13105
|
+
const days = Number(opts.days);
|
|
13106
|
+
if (!Number.isInteger(days) || days < 1 || days > 30) {
|
|
13107
|
+
fail(this, "days must be an integer from 1 to 30", classifyStatus(400));
|
|
13108
|
+
}
|
|
13109
|
+
const result = await getDecisionLatency(requireCredentials(), experimentId, {
|
|
13110
|
+
days,
|
|
13111
|
+
qaSessionId: opts.qaSession
|
|
13112
|
+
});
|
|
13113
|
+
if (!result.ok || !result.data) {
|
|
13114
|
+
fail(
|
|
13115
|
+
this,
|
|
13116
|
+
result.error ?? "Failed to read decision latency",
|
|
13117
|
+
classifyStatus(result.status),
|
|
13118
|
+
result.errorDetails
|
|
13119
|
+
);
|
|
13120
|
+
}
|
|
13121
|
+
const summary = result.data;
|
|
13122
|
+
output(this, summary, () => {
|
|
13123
|
+
const buckets = [
|
|
13124
|
+
["forced", summary.forced],
|
|
13125
|
+
["natural", summary.natural],
|
|
13126
|
+
...summary.qa_session ? [["qa_session", summary.qa_session]] : []
|
|
13127
|
+
];
|
|
13128
|
+
const ms = (value) => value === null ? "-" : String(Math.round(value));
|
|
13129
|
+
printTable(
|
|
13130
|
+
[
|
|
13131
|
+
"Bucket",
|
|
13132
|
+
"Samples",
|
|
13133
|
+
"Decision p50",
|
|
13134
|
+
"p95",
|
|
13135
|
+
"p99",
|
|
13136
|
+
"Config p50",
|
|
13137
|
+
"p95",
|
|
13138
|
+
"inline/cache/network/none"
|
|
13139
|
+
],
|
|
13140
|
+
buckets.map(([name, bucket]) => [
|
|
13141
|
+
name,
|
|
13142
|
+
String(bucket.samples),
|
|
13143
|
+
ms(bucket.decision_latency_ms.p50),
|
|
13144
|
+
ms(bucket.decision_latency_ms.p95),
|
|
13145
|
+
ms(bucket.decision_latency_ms.p99),
|
|
13146
|
+
ms(bucket.config_fetch_ms.p50),
|
|
13147
|
+
ms(bucket.config_fetch_ms.p95),
|
|
13148
|
+
[
|
|
13149
|
+
bucket.config_sources.inline,
|
|
13150
|
+
bucket.config_sources.cache,
|
|
13151
|
+
bucket.config_sources.network,
|
|
13152
|
+
bucket.config_sources.none
|
|
13153
|
+
].join("/")
|
|
13154
|
+
])
|
|
13155
|
+
);
|
|
13156
|
+
console.log(
|
|
13157
|
+
"Times in ms. Fewer than 5 measured samples: -. Preload/anti-flicker position: under evaluation."
|
|
13158
|
+
);
|
|
13159
|
+
});
|
|
13160
|
+
});
|
|
13099
13161
|
|
|
13100
13162
|
// src/commands/goals.ts
|
|
13101
13163
|
init_source();
|