make-mp-data 3.0.3 → 3.0.5
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 +46 -0
- package/dungeons/array-of-object-lookup-schema.json +327 -0
- package/dungeons/array-of-object-lookup.js +29 -9
- package/dungeons/capstone/capstone-ic3.js +291 -0
- package/dungeons/capstone/capstone-ic4.js +598 -0
- package/dungeons/capstone/capstone-ic5.js +668 -0
- package/dungeons/capstone/generate-product-lookup.js +309 -0
- package/dungeons/ecommerce-schema.json +462 -0
- package/dungeons/{copilot.js → ecommerce.js} +79 -17
- package/dungeons/education-schema.json +2409 -0
- package/dungeons/education.js +226 -462
- package/dungeons/fintech-schema.json +14034 -0
- package/dungeons/fintech.js +134 -413
- package/dungeons/foobar-schema.json +403 -0
- package/dungeons/foobar.js +27 -4
- package/dungeons/food-delivery-schema.json +192 -0
- package/dungeons/food-delivery.js +602 -0
- package/dungeons/food-schema.json +1152 -0
- package/dungeons/food.js +173 -406
- package/dungeons/gaming-schema.json +1270 -0
- package/dungeons/gaming.js +182 -42
- package/dungeons/insurance-application-schema.json +204 -0
- package/dungeons/insurance-application.js +605 -0
- package/dungeons/media-schema.json +906 -0
- package/dungeons/media.js +250 -420
- package/dungeons/retention-cadence-schema.json +78 -0
- package/dungeons/retention-cadence.js +35 -1
- package/dungeons/rpg-schema.json +4526 -0
- package/dungeons/rpg.js +171 -429
- package/dungeons/sanity-schema.json +255 -0
- package/dungeons/sanity.js +21 -10
- package/dungeons/sass-schema.json +1291 -0
- package/dungeons/sass.js +241 -368
- package/dungeons/scd-schema.json +919 -0
- package/dungeons/scd.js +41 -13
- package/dungeons/simple-schema.json +608 -0
- package/dungeons/simple.js +52 -15
- package/dungeons/simplest-schema.json +1418 -0
- package/dungeons/simplest.js +392 -0
- package/dungeons/social-schema.json +1118 -0
- package/dungeons/social.js +150 -391
- package/dungeons/text-generation-schema.json +3096 -0
- package/dungeons/text-generation.js +71 -0
- package/index.js +8 -6
- package/lib/core/config-validator.js +28 -8
- package/lib/core/storage.js +5 -5
- package/lib/generators/events.js +4 -4
- package/lib/orchestrators/mixpanel-sender.js +16 -13
- package/lib/orchestrators/user-loop.js +14 -6
- package/lib/templates/soup-presets.js +188 -0
- package/lib/utils/utils.js +52 -6
- package/package.json +1 -1
- package/types.d.ts +20 -3
- package/dungeons/adspend.js +0 -130
- package/dungeons/anon.js +0 -128
- package/dungeons/benchmark-heavy.js +0 -240
- package/dungeons/benchmark-light.js +0 -140
- package/dungeons/big.js +0 -226
- package/dungeons/business.js +0 -391
- package/dungeons/complex.js +0 -428
- package/dungeons/experiments.js +0 -137
- package/dungeons/funnels.js +0 -309
- package/dungeons/mil.js +0 -323
- package/dungeons/mirror.js +0 -161
- package/dungeons/soup-test.js +0 -52
- package/dungeons/streaming.js +0 -372
- package/dungeons/strict-event-test.js +0 -30
- package/dungeons/student-teacher.js +0 -438
- package/dungeons/too-big-events.js +0 -203
- package/dungeons/user-agent.js +0 -209
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import utc from "dayjs/plugin/utc.js";
|
|
3
|
+
import "dotenv/config";
|
|
4
|
+
import * as u from "../lib/utils/utils.js";
|
|
5
|
+
|
|
6
|
+
const SEED = "dm4-insurance";
|
|
7
|
+
dayjs.extend(utc);
|
|
8
|
+
const chance = u.initChance(SEED);
|
|
9
|
+
const num_users = 5_000;
|
|
10
|
+
const days = 100;
|
|
11
|
+
|
|
12
|
+
/** @typedef {import("../types").Dungeon} Config */
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* ===================================================================
|
|
16
|
+
* DATASET OVERVIEW
|
|
17
|
+
* ===================================================================
|
|
18
|
+
*
|
|
19
|
+
* SAFEHAVEN INSURANCE — Web Application Dungeon
|
|
20
|
+
*
|
|
21
|
+
* SafeHaven Insurance is a modern insurance application where users
|
|
22
|
+
* browse coverage options, request quotes, complete multi-step
|
|
23
|
+
* applications, manage policies, file claims, and make premium payments.
|
|
24
|
+
*
|
|
25
|
+
* - 5,000 users over 100 days
|
|
26
|
+
* - 600,000 events across 18 event types
|
|
27
|
+
* - 5 funnels (onboarding, application, approval, claims, renewal)
|
|
28
|
+
* - 5 insurance types as super property (auto, home, life, health, renters)
|
|
29
|
+
* - Deterministic app versioning (2.10 → 2.11 → 2.12 → 2.13)
|
|
30
|
+
* - Platforms: web, iOS, Android
|
|
31
|
+
*
|
|
32
|
+
* CORE LOOP:
|
|
33
|
+
* Users create an account, browse insurance products, and request quotes.
|
|
34
|
+
* They start multi-step applications (personal info, coverage selection,
|
|
35
|
+
* document upload) and submit for approval. Once approved, they activate
|
|
36
|
+
* a policy, make premium payments, and manage renewals. If something
|
|
37
|
+
* goes wrong, they file claims and create support tickets.
|
|
38
|
+
*
|
|
39
|
+
* KEY DATA STORY — VERSION 2.13 RELEASE:
|
|
40
|
+
* The app has gone through versions 2.10 → 2.11 → 2.12 → 2.13.
|
|
41
|
+
* Version 2.13 was released 10 days ago and fixed critical UX issues.
|
|
42
|
+
* Two effects are visible: support ticket volume drops immediately,
|
|
43
|
+
* and application funnel conversion improves significantly.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* ===================================================================
|
|
48
|
+
* ANALYTICS HOOKS
|
|
49
|
+
* ===================================================================
|
|
50
|
+
*
|
|
51
|
+
* -------------------------------------------------------------------
|
|
52
|
+
* 1. VERSION STAMPING (event hook)
|
|
53
|
+
* -------------------------------------------------------------------
|
|
54
|
+
* Every event gets a deterministic app_version based on its timestamp.
|
|
55
|
+
* All users shift simultaneously on release dates:
|
|
56
|
+
* - Days 0-30: v2.10
|
|
57
|
+
* - Days 30-60: v2.11
|
|
58
|
+
* - Days 60-90: v2.12
|
|
59
|
+
* - Last 10 days: v2.13
|
|
60
|
+
*
|
|
61
|
+
* MIXPANEL REPORT:
|
|
62
|
+
* 1. Insights > "page viewed" > Breakdown by app_version
|
|
63
|
+
* 2. Chart event volume over time, colored by app_version
|
|
64
|
+
* 3. Confirm: no overlap between versions (deterministic cutover)
|
|
65
|
+
*
|
|
66
|
+
* -------------------------------------------------------------------
|
|
67
|
+
* 2. SUPPORT TICKET VOLUME DROP (everything hook)
|
|
68
|
+
* -------------------------------------------------------------------
|
|
69
|
+
* Before v2.13, support ticket volume is high — each user gets 2-3
|
|
70
|
+
* extra tickets injected with bug-related categories (form_crash,
|
|
71
|
+
* login_error, page_timeout, payment_failure). After v2.13, tickets
|
|
72
|
+
* are progressively removed (30% on day 1 → 85% on day 10).
|
|
73
|
+
*
|
|
74
|
+
* MIXPANEL REPORT:
|
|
75
|
+
* 1. Insights > "support ticket created" count over time (line chart)
|
|
76
|
+
* 2. Break down by app_version: v2.12 has high volume, v2.13 drops
|
|
77
|
+
* 3. Filter issue_category to bug categories (form_crash, etc.)
|
|
78
|
+
* 4. Filter pre_release_bug = true for injected tickets only
|
|
79
|
+
* 5. Compare weekly ticket volume before vs after v2.13 release
|
|
80
|
+
*
|
|
81
|
+
* -------------------------------------------------------------------
|
|
82
|
+
* 3. APPLICATION CONVERSION BOOST (everything hook)
|
|
83
|
+
* -------------------------------------------------------------------
|
|
84
|
+
* Before v2.13, 40% of "application approved" and "policy activated"
|
|
85
|
+
* events are removed, simulating a broken application flow. After
|
|
86
|
+
* v2.13, all events are preserved — creating a visible conversion jump.
|
|
87
|
+
*
|
|
88
|
+
* MIXPANEL REPORT:
|
|
89
|
+
* 1. Funnels > application submitted → approved → policy activated
|
|
90
|
+
* 2. Break down by app_version (v2.12 vs v2.13)
|
|
91
|
+
* 3. Compare conversion rates: pre-v2.13 ~60% of post-v2.13
|
|
92
|
+
* 4. Insights > "application approved" count over time — step change
|
|
93
|
+
*
|
|
94
|
+
* ===================================================================
|
|
95
|
+
* EXPECTED METRICS SUMMARY
|
|
96
|
+
* ===================================================================
|
|
97
|
+
*
|
|
98
|
+
* Hook | Metric | Pre-v2.13 | Post-v2.13
|
|
99
|
+
* ------------------------|-------------------------|-----------|----------
|
|
100
|
+
* Version Stamping | Events per version | ~30d each | 10 days
|
|
101
|
+
* Support Ticket Volume | Weekly ticket count | HIGH | ~70% lower
|
|
102
|
+
* Application Conversion | Approval rate | ~42% | ~70%
|
|
103
|
+
*
|
|
104
|
+
* ===================================================================
|
|
105
|
+
* ADVANCED ANALYSIS IDEAS
|
|
106
|
+
* ===================================================================
|
|
107
|
+
*
|
|
108
|
+
* 1. Version Impact Dashboard: Chart both support tickets AND
|
|
109
|
+
* application conversion by app_version to show v2.13's dual impact.
|
|
110
|
+
*
|
|
111
|
+
* 2. Bug Category Analysis: Which pre_release_bug categories were most
|
|
112
|
+
* common? Do they correlate with the application steps where users
|
|
113
|
+
* were dropping off?
|
|
114
|
+
*
|
|
115
|
+
* 3. Platform Comparison: Did the v2.13 improvement affect all platforms
|
|
116
|
+
* equally, or did web/iOS/Android see different magnitudes?
|
|
117
|
+
*
|
|
118
|
+
* 4. Insurance Type Breakdown: Are certain insurance types (auto vs home
|
|
119
|
+
* vs life) more affected by the conversion improvement?
|
|
120
|
+
*
|
|
121
|
+
* 5. Time-to-Approval: Did v2.13 also change the approval_time_hours
|
|
122
|
+
* distribution, or just the volume of approvals?
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
// ── Time constants for hook calculations ──
|
|
126
|
+
const NOW = dayjs();
|
|
127
|
+
const DATASET_START = NOW.subtract(days, "days");
|
|
128
|
+
const V211_DATE = DATASET_START.add(30, "days");
|
|
129
|
+
const V212_DATE = DATASET_START.add(60, "days");
|
|
130
|
+
const V213_DATE = NOW.subtract(10, "days");
|
|
131
|
+
|
|
132
|
+
/** @type {Config} */
|
|
133
|
+
const config = {
|
|
134
|
+
token: "3651ac6819e284fbf528d86036eec785",
|
|
135
|
+
seed: SEED,
|
|
136
|
+
numDays: days,
|
|
137
|
+
numEvents: num_users * 120,
|
|
138
|
+
numUsers: num_users,
|
|
139
|
+
hasAnonIds: false,
|
|
140
|
+
hasSessionIds: true,
|
|
141
|
+
format: "json",
|
|
142
|
+
gzip: true,
|
|
143
|
+
alsoInferFunnels: false,
|
|
144
|
+
hasLocation: true,
|
|
145
|
+
hasAndroidDevices: true,
|
|
146
|
+
hasIOSDevices: true,
|
|
147
|
+
hasDesktopDevices: true,
|
|
148
|
+
hasBrowser: false,
|
|
149
|
+
hasCampaigns: false,
|
|
150
|
+
isAnonymous: false,
|
|
151
|
+
hasAdSpend: false,
|
|
152
|
+
percentUsersBornInDataset: 35,
|
|
153
|
+
hasAvatar: true,
|
|
154
|
+
batchSize: 2_500_000,
|
|
155
|
+
concurrency: 1,
|
|
156
|
+
writeToDisk: false,
|
|
157
|
+
|
|
158
|
+
scdProps: {},
|
|
159
|
+
mirrorProps: {},
|
|
160
|
+
lookupTables: [],
|
|
161
|
+
|
|
162
|
+
// ── Events ──
|
|
163
|
+
events: [
|
|
164
|
+
{
|
|
165
|
+
event: "account created",
|
|
166
|
+
weight: 1,
|
|
167
|
+
isFirstEvent: true,
|
|
168
|
+
properties: {
|
|
169
|
+
signup_source: ["web", "mobile", "agent_referral", "partner"],
|
|
170
|
+
account_type: ["individual", "family", "business"],
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
event: "page viewed",
|
|
175
|
+
weight: 10,
|
|
176
|
+
properties: {
|
|
177
|
+
page_name: [
|
|
178
|
+
"home",
|
|
179
|
+
"quotes",
|
|
180
|
+
"coverage_options",
|
|
181
|
+
"claims",
|
|
182
|
+
"faq",
|
|
183
|
+
"profile",
|
|
184
|
+
"payment",
|
|
185
|
+
"documents",
|
|
186
|
+
],
|
|
187
|
+
referrer: ["direct", "google", "email", "social_media"],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
event: "quote requested",
|
|
192
|
+
weight: 5,
|
|
193
|
+
properties: {
|
|
194
|
+
coverage_level: ["basic", "standard", "premium"],
|
|
195
|
+
deductible: u.weighNumRange(250, 5000, 0.5, 1000),
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
event: "quote received",
|
|
200
|
+
weight: 4,
|
|
201
|
+
properties: {
|
|
202
|
+
monthly_premium: u.weighNumRange(30, 800, 0.3, 150),
|
|
203
|
+
coverage_amount: u.weighNumRange(10000, 500000, 0.3, 100000),
|
|
204
|
+
quote_comparison_count: u.weighNumRange(1, 5, 0.5, 2),
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
event: "application started",
|
|
209
|
+
weight: 4,
|
|
210
|
+
properties: {
|
|
211
|
+
coverage_level: ["basic", "standard", "premium"],
|
|
212
|
+
estimated_premium: u.weighNumRange(30, 800, 0.3, 150),
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
event: "application step completed",
|
|
217
|
+
weight: 6,
|
|
218
|
+
properties: {
|
|
219
|
+
step_name: [
|
|
220
|
+
"personal_info",
|
|
221
|
+
"coverage_selection",
|
|
222
|
+
"medical_history",
|
|
223
|
+
"vehicle_info",
|
|
224
|
+
"beneficiary",
|
|
225
|
+
"review",
|
|
226
|
+
],
|
|
227
|
+
step_number: u.weighNumRange(1, 6),
|
|
228
|
+
time_on_step_sec: u.weighNumRange(15, 600, 0.3, 90),
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
event: "document uploaded",
|
|
233
|
+
weight: 3,
|
|
234
|
+
properties: {
|
|
235
|
+
document_type: [
|
|
236
|
+
"drivers_license",
|
|
237
|
+
"proof_of_address",
|
|
238
|
+
"vehicle_registration",
|
|
239
|
+
"medical_records",
|
|
240
|
+
"property_photos",
|
|
241
|
+
],
|
|
242
|
+
file_size_kb: u.weighNumRange(50, 5000, 0.5, 500),
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
event: "application submitted",
|
|
247
|
+
weight: 3,
|
|
248
|
+
properties: {
|
|
249
|
+
documents_attached: u.weighNumRange(1, 5, 0.5, 2),
|
|
250
|
+
application_time_min: u.weighNumRange(5, 60, 0.3, 15),
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
event: "application approved",
|
|
255
|
+
weight: 2,
|
|
256
|
+
properties: {
|
|
257
|
+
approved_premium: u.weighNumRange(30, 800, 0.3, 150),
|
|
258
|
+
approval_time_hours: u.weighNumRange(1, 72, 0.5, 24),
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
event: "policy activated",
|
|
263
|
+
weight: 2,
|
|
264
|
+
properties: {
|
|
265
|
+
policy_term_months: [6, 12, 12, 12, 24],
|
|
266
|
+
effective_date_offset_days: u.weighNumRange(0, 30, 0.5, 7),
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
event: "claim filed",
|
|
271
|
+
weight: 2,
|
|
272
|
+
properties: {
|
|
273
|
+
claim_type: [
|
|
274
|
+
"collision",
|
|
275
|
+
"theft",
|
|
276
|
+
"water_damage",
|
|
277
|
+
"fire",
|
|
278
|
+
"medical",
|
|
279
|
+
"liability",
|
|
280
|
+
],
|
|
281
|
+
estimated_amount: u.weighNumRange(100, 50000, 0.3, 3000),
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
event: "claim status checked",
|
|
286
|
+
weight: 4,
|
|
287
|
+
properties: {
|
|
288
|
+
claim_status: [
|
|
289
|
+
"submitted",
|
|
290
|
+
"under_review",
|
|
291
|
+
"approved",
|
|
292
|
+
"denied",
|
|
293
|
+
"payment_pending",
|
|
294
|
+
],
|
|
295
|
+
days_since_filed: u.weighNumRange(1, 60, 0.5, 10),
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
event: "payment made",
|
|
300
|
+
weight: 5,
|
|
301
|
+
properties: {
|
|
302
|
+
payment_method: ["credit_card", "bank_transfer", "auto_pay", "check"],
|
|
303
|
+
amount: u.weighNumRange(30, 800, 0.3, 150),
|
|
304
|
+
payment_status: ["success", "success", "success", "success", "failed"],
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
event: "support ticket created",
|
|
309
|
+
weight: 4,
|
|
310
|
+
properties: {
|
|
311
|
+
issue_category: [
|
|
312
|
+
"billing",
|
|
313
|
+
"claims",
|
|
314
|
+
"coverage",
|
|
315
|
+
"technical",
|
|
316
|
+
"policy_change",
|
|
317
|
+
],
|
|
318
|
+
priority: ["low", "medium", "medium", "high"],
|
|
319
|
+
channel: ["chat", "phone", "email", "web_form"],
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
event: "support ticket resolved",
|
|
324
|
+
weight: 3,
|
|
325
|
+
properties: {
|
|
326
|
+
resolution_type: [
|
|
327
|
+
"self_service",
|
|
328
|
+
"agent_assisted",
|
|
329
|
+
"escalated",
|
|
330
|
+
"auto_resolved",
|
|
331
|
+
],
|
|
332
|
+
satisfaction_score: u.weighNumRange(1, 5, 1, 4),
|
|
333
|
+
resolution_time_hours: u.weighNumRange(0.5, 72, 0.3, 8),
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
event: "coverage reviewed",
|
|
338
|
+
weight: 4,
|
|
339
|
+
properties: {
|
|
340
|
+
current_premium: u.weighNumRange(30, 800, 0.3, 150),
|
|
341
|
+
coverage_adequate: [true, true, true, false],
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
event: "profile updated",
|
|
346
|
+
weight: 2,
|
|
347
|
+
properties: {
|
|
348
|
+
field_updated: [
|
|
349
|
+
"address",
|
|
350
|
+
"phone",
|
|
351
|
+
"email",
|
|
352
|
+
"beneficiary",
|
|
353
|
+
"payment_method",
|
|
354
|
+
"vehicle_info",
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
event: "renewal completed",
|
|
360
|
+
weight: 2,
|
|
361
|
+
properties: {
|
|
362
|
+
renewal_premium: u.weighNumRange(30, 800, 0.3, 150),
|
|
363
|
+
premium_change_pct: u.weighNumRange(-15, 20, 1, 3),
|
|
364
|
+
auto_renewed: [true, true, false],
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
|
|
369
|
+
// ── Funnels ──
|
|
370
|
+
funnels: [
|
|
371
|
+
{
|
|
372
|
+
name: "Onboarding",
|
|
373
|
+
sequence: ["account created", "page viewed", "quote requested"],
|
|
374
|
+
isFirstFunnel: true,
|
|
375
|
+
conversionRate: 85,
|
|
376
|
+
timeToConvert: 0.5,
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "Application Completion",
|
|
380
|
+
sequence: [
|
|
381
|
+
"application started",
|
|
382
|
+
"application step completed",
|
|
383
|
+
"document uploaded",
|
|
384
|
+
"application submitted",
|
|
385
|
+
],
|
|
386
|
+
conversionRate: 60,
|
|
387
|
+
timeToConvert: 48,
|
|
388
|
+
weight: 4,
|
|
389
|
+
order: "sequential",
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: "Application Approval",
|
|
393
|
+
sequence: [
|
|
394
|
+
"application submitted",
|
|
395
|
+
"application approved",
|
|
396
|
+
"policy activated",
|
|
397
|
+
],
|
|
398
|
+
conversionRate: 70,
|
|
399
|
+
timeToConvert: 72,
|
|
400
|
+
weight: 3,
|
|
401
|
+
order: "sequential",
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: "Claims Process",
|
|
405
|
+
sequence: [
|
|
406
|
+
"claim filed",
|
|
407
|
+
"claim status checked",
|
|
408
|
+
"support ticket created",
|
|
409
|
+
],
|
|
410
|
+
conversionRate: 50,
|
|
411
|
+
timeToConvert: 24,
|
|
412
|
+
weight: 2,
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
name: "Policy Renewal",
|
|
416
|
+
sequence: ["coverage reviewed", "payment made", "renewal completed"],
|
|
417
|
+
conversionRate: 65,
|
|
418
|
+
timeToConvert: 72,
|
|
419
|
+
weight: 3,
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
|
|
423
|
+
// ── Super Props (on every event) ──
|
|
424
|
+
superProps: {
|
|
425
|
+
platform: ["web", "ios", "android"],
|
|
426
|
+
insurance_type: ["auto", "home", "life", "health", "renters"],
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
// ── User Props (set once per user) ──
|
|
430
|
+
userProps: {
|
|
431
|
+
age_range: ["18-25", "26-35", "36-45", "46-55", "56-65", "65+"],
|
|
432
|
+
risk_profile: ["low", "medium", "high"],
|
|
433
|
+
policy_count: u.weighNumRange(0, 5, 0.5, 1),
|
|
434
|
+
lifetime_premium: u.weighNumRange(0, 50000, 0.3, 5000),
|
|
435
|
+
preferred_contact: ["email", "phone", "app_notification"],
|
|
436
|
+
},
|
|
437
|
+
|
|
438
|
+
// ── Hook Function ──
|
|
439
|
+
/**
|
|
440
|
+
* ARCHITECTED ANALYTICS HOOKS
|
|
441
|
+
*
|
|
442
|
+
* This hook function creates 3 deliberate patterns in the data:
|
|
443
|
+
*
|
|
444
|
+
* 1. VERSION STAMPING (event): Every event gets a deterministic app_version
|
|
445
|
+
* based on its timestamp. v2.10 → v2.11 → v2.12 → v2.13.
|
|
446
|
+
* All users shift simultaneously on release dates.
|
|
447
|
+
*
|
|
448
|
+
* 2. SUPPORT TICKET VOLUME (everything): Pre-v2.13 period has inflated
|
|
449
|
+
* support ticket volume (2-3 extra tickets per user with bug-related
|
|
450
|
+
* categories). Post-v2.13, tickets are progressively removed — creating
|
|
451
|
+
* a clear volume drop that trends downward.
|
|
452
|
+
*
|
|
453
|
+
* 3. APPLICATION CONVERSION BOOST (everything): Pre-v2.13, ~40% of
|
|
454
|
+
* application approved and policy activated events are removed,
|
|
455
|
+
* lowering the effective funnel conversion rate. Post-v2.13 events
|
|
456
|
+
* are left intact, making the conversion visibly jump up.
|
|
457
|
+
*/
|
|
458
|
+
hook: function (record, type, meta) {
|
|
459
|
+
// =============================================================
|
|
460
|
+
// Hook #1: VERSION STAMPING (event)
|
|
461
|
+
// Deterministic app_version on every event based on timestamp.
|
|
462
|
+
// v2.10 (days 0-30) → v2.11 (30-60) → v2.12 (60-90) → v2.13 (last 10 days)
|
|
463
|
+
// =============================================================
|
|
464
|
+
if (type === "event") {
|
|
465
|
+
const eventTime = dayjs(record.time);
|
|
466
|
+
|
|
467
|
+
if (eventTime.isBefore(V211_DATE)) {
|
|
468
|
+
record.app_version = "2.10";
|
|
469
|
+
} else if (eventTime.isBefore(V212_DATE)) {
|
|
470
|
+
record.app_version = "2.11";
|
|
471
|
+
} else if (eventTime.isBefore(V213_DATE)) {
|
|
472
|
+
record.app_version = "2.12";
|
|
473
|
+
} else {
|
|
474
|
+
record.app_version = "2.13";
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// =============================================================
|
|
479
|
+
// Hook #2 & #3: SUPPORT TICKET VOLUME + APPLICATION CONVERSION
|
|
480
|
+
// (everything)
|
|
481
|
+
// =============================================================
|
|
482
|
+
if (type === "everything") {
|
|
483
|
+
const userEvents = record;
|
|
484
|
+
if (userEvents.length === 0) return record;
|
|
485
|
+
|
|
486
|
+
// Find a user_id from any existing event
|
|
487
|
+
const userId =
|
|
488
|
+
userEvents.find((e) => e.user_id)?.user_id ||
|
|
489
|
+
userEvents[0]?.device_id;
|
|
490
|
+
|
|
491
|
+
// ─── Hook #2: SUPPORT TICKET VOLUME ───
|
|
492
|
+
// PRE-V2.13: Inject 2-3 extra support tickets with bug-related categories
|
|
493
|
+
const preV213Tickets = userEvents.filter(
|
|
494
|
+
(e) =>
|
|
495
|
+
e.event === "support ticket created" &&
|
|
496
|
+
dayjs(e.time).isBefore(V213_DATE)
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
if (preV213Tickets.length > 0) {
|
|
500
|
+
const extraCount = chance.integer({ min: 2, max: 3 });
|
|
501
|
+
const bugCategories = [
|
|
502
|
+
"form_crash",
|
|
503
|
+
"login_error",
|
|
504
|
+
"page_timeout",
|
|
505
|
+
"payment_failure",
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
for (let i = 0; i < extraCount; i++) {
|
|
509
|
+
// Pick a random pre-v2.13 ticket to base timing on
|
|
510
|
+
const sourceTicket = chance.pickone(preV213Tickets);
|
|
511
|
+
const sourceTime = dayjs(sourceTicket.time);
|
|
512
|
+
// Offset by a few hours to days
|
|
513
|
+
const offsetHours = chance.integer({ min: 1, max: 72 });
|
|
514
|
+
let newTime = sourceTime.add(offsetHours, "hours");
|
|
515
|
+
// Ensure it stays before v2.13
|
|
516
|
+
if (newTime.isAfter(V213_DATE)) {
|
|
517
|
+
newTime = V213_DATE.subtract(
|
|
518
|
+
chance.integer({ min: 1, max: 48 }),
|
|
519
|
+
"hours"
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Compute app_version for injected event
|
|
524
|
+
let injectedVersion;
|
|
525
|
+
if (newTime.isBefore(V211_DATE)) {
|
|
526
|
+
injectedVersion = "2.10";
|
|
527
|
+
} else if (newTime.isBefore(V212_DATE)) {
|
|
528
|
+
injectedVersion = "2.11";
|
|
529
|
+
} else {
|
|
530
|
+
injectedVersion = "2.12"; // always pre-v2.13
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
userEvents.push({
|
|
534
|
+
event: "support ticket created",
|
|
535
|
+
time: newTime.toISOString(),
|
|
536
|
+
user_id: userId,
|
|
537
|
+
app_version: injectedVersion,
|
|
538
|
+
issue_category: chance.pickone(bugCategories),
|
|
539
|
+
priority: chance.pickone(["medium", "high", "high"]),
|
|
540
|
+
channel: chance.pickone([
|
|
541
|
+
"chat",
|
|
542
|
+
"phone",
|
|
543
|
+
"email",
|
|
544
|
+
"web_form",
|
|
545
|
+
]),
|
|
546
|
+
pre_release_bug: true,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// POST-V2.13: Remove support tickets with increasing probability
|
|
552
|
+
// Day 1 after release: ~30% removal
|
|
553
|
+
// Day 5: ~60% removal
|
|
554
|
+
// Day 10: ~85% removal
|
|
555
|
+
for (let i = userEvents.length - 1; i >= 0; i--) {
|
|
556
|
+
const evt = userEvents[i];
|
|
557
|
+
if (evt.event === "support ticket created") {
|
|
558
|
+
const evtTime = dayjs(evt.time);
|
|
559
|
+
if (evtTime.isAfter(V213_DATE)) {
|
|
560
|
+
const daysSinceRelease = evtTime.diff(
|
|
561
|
+
V213_DATE,
|
|
562
|
+
"days",
|
|
563
|
+
true
|
|
564
|
+
);
|
|
565
|
+
// Linear ramp: 30% base + 5.5% per day → ~85% at day 10
|
|
566
|
+
const removalLikelihood = Math.min(
|
|
567
|
+
85,
|
|
568
|
+
30 + daysSinceRelease * 5.5
|
|
569
|
+
);
|
|
570
|
+
if (chance.bool({ likelihood: removalLikelihood })) {
|
|
571
|
+
userEvents.splice(i, 1);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// ─── Hook #3: APPLICATION CONVERSION BOOST ───
|
|
578
|
+
// PRE-V2.13: Remove ~40% of application approved and policy activated
|
|
579
|
+
// events, lowering effective conversion before the release.
|
|
580
|
+
for (let i = userEvents.length - 1; i >= 0; i--) {
|
|
581
|
+
const evt = userEvents[i];
|
|
582
|
+
if (
|
|
583
|
+
(evt.event === "application approved" ||
|
|
584
|
+
evt.event === "policy activated") &&
|
|
585
|
+
dayjs(evt.time).isBefore(V213_DATE)
|
|
586
|
+
) {
|
|
587
|
+
if (chance.bool({ likelihood: 40 })) {
|
|
588
|
+
userEvents.splice(i, 1);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Sort events by time after injection/removal
|
|
594
|
+
userEvents.sort(
|
|
595
|
+
(a, b) => new Date(a.time) - new Date(b.time)
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
return record;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return record;
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
export default config;
|