@ak--47/dungeon-master 1.1.0 → 1.2.1
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/dungeons/technical/ad-spend-schema.json +2 -2
- package/dungeons/technical/ad-spend.js +16 -5
- package/dungeons/technical/anonymous-users-schema.json +4 -3
- package/dungeons/technical/anonymous-users.js +16 -6
- package/dungeons/technical/array-of-object-lookup-schema.json +1 -1
- package/dungeons/technical/array-of-object-lookup.js +22 -6
- package/dungeons/technical/experiments-schema.json +25 -19
- package/dungeons/technical/experiments.js +24 -6
- package/dungeons/technical/foobar.js +29 -13
- package/dungeons/technical/group-analytics-schema.json +92 -0
- package/dungeons/technical/group-analytics.js +16 -6
- package/dungeons/technical/mirror-strategies-schema.json +4 -4
- package/dungeons/technical/mirror-strategies.js +16 -6
- package/dungeons/technical/nested-objects.js +16 -5
- package/dungeons/technical/retention-cadence.js +14 -9
- package/dungeons/technical/sanity-schema.json +22 -22
- package/dungeons/technical/sanity.js +26 -6
- package/dungeons/technical/scale-test.js +16 -5
- package/dungeons/technical/scd-schema.json +230 -33
- package/dungeons/technical/scd.js +25 -5
- package/dungeons/technical/simple-schema.json +132 -48
- package/dungeons/technical/simple.js +21 -6
- package/dungeons/technical/simplest-schema.json +168 -79
- package/dungeons/technical/simplest.js +24 -7
- package/dungeons/technical/text-generation-schema.json +7 -4
- package/dungeons/technical/text-generation.js +23 -8
- package/dungeons/user/.gitkeep +0 -0
- package/dungeons/user/shalini.js +847 -0
- package/dungeons/vertical/community-schema.json +71 -49
- package/dungeons/vertical/community.js +57 -30
- package/dungeons/vertical/devtools-schema.json +69 -48
- package/dungeons/vertical/devtools.js +58 -31
- package/dungeons/vertical/ecommerce-schema.json +126 -108
- package/dungeons/vertical/ecommerce.js +32 -8
- package/dungeons/vertical/education-schema.json +5261 -272
- package/dungeons/vertical/education.js +45 -22
- package/dungeons/vertical/fintech-schema.json +165 -98
- package/dungeons/vertical/fintech.js +43 -11
- package/dungeons/vertical/fitness-schema.json +69 -47
- package/dungeons/vertical/fitness.js +56 -24
- package/dungeons/vertical/food-delivery-schema.json +36237 -187
- package/dungeons/vertical/food-delivery.js +67 -15
- package/dungeons/vertical/gaming-schema.json +104 -60
- package/dungeons/vertical/gaming.js +61 -16
- package/dungeons/vertical/healthcare-schema.json +70 -48
- package/dungeons/vertical/healthcare.js +77 -42
- package/dungeons/vertical/insurance-application-schema.json +66 -47
- package/dungeons/vertical/insurance-application.js +34 -10
- package/dungeons/vertical/logistics-schema.json +60 -38
- package/dungeons/vertical/logistics.js +77 -43
- package/dungeons/vertical/marketplace-schema.json +65 -43
- package/dungeons/vertical/marketplace.js +52 -21
- package/dungeons/vertical/media-schema.json +4085 -123
- package/dungeons/vertical/media.js +65 -18
- package/dungeons/vertical/rpg-schema.json +1984 -147
- package/dungeons/vertical/rpg.js +47 -11
- package/dungeons/vertical/sass-schema.json +2622 -147
- package/dungeons/vertical/sass.js +40 -10
- package/dungeons/vertical/social-schema.json +146 -78
- package/dungeons/vertical/social.js +39 -10
- package/dungeons/vertical/travel-schema.json +57 -35
- package/dungeons/vertical/travel.js +58 -21
- package/index.js +4 -1
- package/lib/core/config-validator.js +13 -2
- package/lib/core/context.js +9 -0
- package/lib/core/storage.js +2 -0
- package/lib/generators/events.js +3 -5
- package/lib/orchestrators/user-loop.js +24 -1
- package/lib/utils/utils.js +129 -9
- package/package.json +1 -1
- package/scripts/extract-dungeon-schema.mjs +64 -18
- package/types.d.ts +8 -3
|
@@ -1,16 +1,23 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "dm4-logistics";
|
|
3
|
+
const num_days = 100;
|
|
4
|
+
const num_users = 5_000;
|
|
5
|
+
const avg_events_per_user = 120;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
import dayjs from "dayjs";
|
|
2
12
|
import utc from "dayjs/plugin/utc.js";
|
|
3
13
|
import "dotenv/config";
|
|
4
14
|
import * as u from "../../lib/utils/utils.js";
|
|
5
15
|
import * as v from "ak-tools";
|
|
6
16
|
|
|
7
|
-
const SEED = "dm4-logistics";
|
|
8
17
|
dayjs.extend(utc);
|
|
9
18
|
const chance = u.initChance(SEED);
|
|
10
|
-
const num_users = 5_000;
|
|
11
|
-
const days = 100;
|
|
12
19
|
const NOW = dayjs();
|
|
13
|
-
const DATASET_START = NOW.subtract(
|
|
20
|
+
const DATASET_START = NOW.subtract(num_days, "days");
|
|
14
21
|
|
|
15
22
|
/** @typedef {import("../../types").Dungeon} Config */
|
|
16
23
|
|
|
@@ -216,12 +223,14 @@ const supplierIds = v.range(1, 150).map(() => `SUP_${v.uid(6)}`);
|
|
|
216
223
|
* networks with large logistics teams, driving higher ACV.
|
|
217
224
|
*
|
|
218
225
|
* -------------------------------------------------------------------
|
|
219
|
-
* 8.
|
|
226
|
+
* 8. SMALL-BUSINESS CONVERSION DROP (everything hook)
|
|
220
227
|
* -------------------------------------------------------------------
|
|
221
228
|
*
|
|
222
|
-
* PATTERN:
|
|
223
|
-
*
|
|
224
|
-
*
|
|
229
|
+
* PATTERN: Small-business users lose ~35% of "alert configured"
|
|
230
|
+
* events (last step of the Integration Setup funnel). This is
|
|
231
|
+
* implemented via event filtering in the everything hook rather
|
|
232
|
+
* than conversionRate modification in funnel-pre, so the effect
|
|
233
|
+
* is not diluted by organic (non-funnel) events.
|
|
225
234
|
*
|
|
226
235
|
* HOW TO FIND IT IN MIXPANEL:
|
|
227
236
|
*
|
|
@@ -229,10 +238,10 @@ const supplierIds = v.range(1, 150).map(() => `SUP_${v.uid(6)}`);
|
|
|
229
238
|
* - Report type: Funnels
|
|
230
239
|
* - Steps: "integration connected" -> "report generated" -> "alert configured"
|
|
231
240
|
* - Breakdown: user property "company_tier"
|
|
232
|
-
* - Expected:
|
|
241
|
+
* - Expected: small_business ~20% vs enterprise/mid_market ~30% conversion
|
|
233
242
|
*
|
|
234
|
-
* REAL-WORLD ANALOGUE:
|
|
235
|
-
*
|
|
243
|
+
* REAL-WORLD ANALOGUE: Small businesses lack dedicated IT teams,
|
|
244
|
+
* leading to incomplete integration setup and lower alert adoption.
|
|
236
245
|
*
|
|
237
246
|
* ===================================================================
|
|
238
247
|
* EXPECTED METRICS SUMMARY
|
|
@@ -247,15 +256,15 @@ const supplierIds = v.range(1, 150).map(() => `SUP_${v.uid(6)}`);
|
|
|
247
256
|
* Alert Fatigue | response_time_hours | 4h | 8-12h | 2-3x
|
|
248
257
|
* Trial Churn | events after wk 1 | 5 | 2.5 | 0.5x
|
|
249
258
|
* Enterprise Profiles | warehouse_count | 3 | 10 | 3.3x
|
|
250
|
-
*
|
|
259
|
+
* Small-Biz Conversion Drop | funnel conversion | 30% | 20% | 0.65x
|
|
251
260
|
*/
|
|
252
261
|
|
|
253
262
|
/** @type {Config} */
|
|
254
263
|
const config = {
|
|
255
|
-
token
|
|
264
|
+
token,
|
|
256
265
|
seed: SEED,
|
|
257
|
-
numDays:
|
|
258
|
-
numEvents: num_users *
|
|
266
|
+
numDays: num_days,
|
|
267
|
+
numEvents: num_users * avg_events_per_user,
|
|
259
268
|
numUsers: num_users,
|
|
260
269
|
hasAnonIds: false,
|
|
261
270
|
hasSessionIds: true,
|
|
@@ -272,10 +281,16 @@ const config = {
|
|
|
272
281
|
hasAdSpend: false,
|
|
273
282
|
percentUsersBornInDataset: 35,
|
|
274
283
|
hasAvatar: true,
|
|
275
|
-
batchSize: 2_500_000,
|
|
276
284
|
concurrency: 1,
|
|
277
285
|
writeToDisk: false,
|
|
278
|
-
scdProps: {
|
|
286
|
+
scdProps: {
|
|
287
|
+
company_tier: {
|
|
288
|
+
values: ["trial", "starter", "professional", "enterprise"],
|
|
289
|
+
frequency: "month",
|
|
290
|
+
timing: "fuzzy",
|
|
291
|
+
max: 6
|
|
292
|
+
}
|
|
293
|
+
},
|
|
279
294
|
mirrorProps: {},
|
|
280
295
|
lookupTables: [],
|
|
281
296
|
|
|
@@ -499,7 +514,7 @@ const config = {
|
|
|
499
514
|
|
|
500
515
|
// -- SuperProps ----------------------------------------------------
|
|
501
516
|
superProps: {
|
|
502
|
-
|
|
517
|
+
Platform: ["web", "web", "desktop_app"],
|
|
503
518
|
subscription_plan: ["free_trial", "free_trial", "starter", "starter", "professional", "enterprise"],
|
|
504
519
|
},
|
|
505
520
|
|
|
@@ -510,6 +525,8 @@ const config = {
|
|
|
510
525
|
employee_count: u.weighNumRange(1, 500, 0.3, 25),
|
|
511
526
|
industry: ["retail", "manufacturing", "food_beverage", "pharma", "electronics"],
|
|
512
527
|
integration_count: [0],
|
|
528
|
+
Platform: ["web", "web", "desktop_app"],
|
|
529
|
+
subscription_plan: ["free_trial", "free_trial", "starter", "starter", "professional", "enterprise"],
|
|
513
530
|
},
|
|
514
531
|
|
|
515
532
|
// -- Phase 2: Personas --------------------------------------------
|
|
@@ -691,16 +708,9 @@ const config = {
|
|
|
691
708
|
}
|
|
692
709
|
|
|
693
710
|
// -- HOOK 8: ENTERPRISE INTEGRATION FUNNEL LIFT (funnel-pre) --
|
|
694
|
-
//
|
|
711
|
+
// (conversionRate boost removed — filtering applied in everything hook instead)
|
|
695
712
|
if (type === "funnel-pre") {
|
|
696
|
-
|
|
697
|
-
const tier = meta.profile.company_tier;
|
|
698
|
-
if (tier === "enterprise") {
|
|
699
|
-
record.conversionRate = Math.min(record.conversionRate * 1.5, 95);
|
|
700
|
-
} else if (tier === "mid_market") {
|
|
701
|
-
record.conversionRate = Math.min(record.conversionRate * 1.2, 85);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
713
|
+
// no-op: conversion differentiation handled via event filtering below
|
|
704
714
|
}
|
|
705
715
|
|
|
706
716
|
// -- HOOK 1: MONTH-END REPORTING SURGE (event) ----------------
|
|
@@ -722,29 +732,53 @@ const config = {
|
|
|
722
732
|
|
|
723
733
|
// -- EVERYTHING HOOKS -----------------------------------------
|
|
724
734
|
if (type === "everything") {
|
|
725
|
-
|
|
726
|
-
|
|
735
|
+
if (!record.length) return record;
|
|
736
|
+
const profile = meta.profile;
|
|
737
|
+
|
|
738
|
+
// -- SUPER-PROP STAMPING ----------------------------------
|
|
739
|
+
// Stamp superProps from profile so they are consistent per-user.
|
|
740
|
+
if (profile) {
|
|
741
|
+
const plat = profile.Platform;
|
|
742
|
+
const plan = profile.subscription_plan;
|
|
743
|
+
record.forEach(e => {
|
|
744
|
+
if (plat) e.Platform = plat;
|
|
745
|
+
if (plan) e.subscription_plan = plan;
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// -- HOOK 8: SMALL-BUSINESS CONVERSION DROP ---------------
|
|
750
|
+
// Small-business users lose ~35% of "alert configured" events
|
|
751
|
+
// (last step of Integration Setup funnel), simulating lower
|
|
752
|
+
// conversion for smaller teams without dedicated IT resources.
|
|
753
|
+
if (profile && profile.company_tier === "small_business") {
|
|
754
|
+
record = record.filter(e => {
|
|
755
|
+
if (e.event === "alert configured" && chance.bool({ likelihood: 35 })) {
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
return true;
|
|
759
|
+
});
|
|
760
|
+
}
|
|
727
761
|
|
|
728
762
|
// -- HOOK 3: REORDER ACCURACY BY TIER ---------------------
|
|
729
763
|
// Enterprise users get 10% of stockout alerts removed.
|
|
730
|
-
if (
|
|
731
|
-
for (let i =
|
|
732
|
-
if (
|
|
733
|
-
|
|
764
|
+
if (profile && profile.company_tier === "enterprise") {
|
|
765
|
+
for (let i = record.length - 1; i >= 0; i--) {
|
|
766
|
+
if (record[i].event === "stockout alert" && chance.bool({ likelihood: 10 })) {
|
|
767
|
+
record.splice(i, 1);
|
|
734
768
|
}
|
|
735
769
|
}
|
|
736
770
|
}
|
|
737
771
|
|
|
738
772
|
// -- HOOK 4: INTEGRATION COMPLETION DRIVES RETENTION ------
|
|
739
773
|
// Users with 3+ integration events get cloned report events.
|
|
740
|
-
const integrationCount =
|
|
774
|
+
const integrationCount = record.filter(e => e.event === "integration connected").length;
|
|
741
775
|
if (integrationCount >= 3) {
|
|
742
|
-
const templateReport =
|
|
776
|
+
const templateReport = record.find(e => e.event === "report generated");
|
|
743
777
|
if (templateReport) {
|
|
744
|
-
const integrationEvents =
|
|
778
|
+
const integrationEvents = record.filter(e => e.event === "integration connected");
|
|
745
779
|
integrationEvents.forEach(ie => {
|
|
746
780
|
if (chance.bool({ likelihood: 65 })) {
|
|
747
|
-
|
|
781
|
+
record.push({
|
|
748
782
|
...templateReport,
|
|
749
783
|
time: dayjs(ie.time).add(chance.integer({ min: 1, max: 5 }), "days").toISOString(),
|
|
750
784
|
user_id: ie.user_id,
|
|
@@ -758,7 +792,7 @@ const config = {
|
|
|
758
792
|
|
|
759
793
|
// -- HOOK 5: ALERT FATIGUE --------------------------------
|
|
760
794
|
// Users with >30 stockout alerts get increasing response times.
|
|
761
|
-
const alertEvents =
|
|
795
|
+
const alertEvents = record.filter(e => e.event === "stockout alert");
|
|
762
796
|
if (alertEvents.length > 30) {
|
|
763
797
|
alertEvents.forEach((alert, idx) => {
|
|
764
798
|
if (idx >= 20) {
|
|
@@ -770,14 +804,14 @@ const config = {
|
|
|
770
804
|
|
|
771
805
|
// -- HOOK 6: TRIAL CHURN ----------------------------------
|
|
772
806
|
// Users with <10 events lose 50% after day 7.
|
|
773
|
-
if (
|
|
774
|
-
const userFirstEvent =
|
|
807
|
+
if (record.length < 10) {
|
|
808
|
+
const userFirstEvent = record[0];
|
|
775
809
|
if (userFirstEvent) {
|
|
776
810
|
const firstTime = dayjs(userFirstEvent.time);
|
|
777
811
|
const cutoff = firstTime.add(7, "days");
|
|
778
|
-
for (let i =
|
|
779
|
-
if (dayjs(
|
|
780
|
-
|
|
812
|
+
for (let i = record.length - 1; i >= 0; i--) {
|
|
813
|
+
if (dayjs(record[i].time).isAfter(cutoff) && chance.bool({ likelihood: 50 })) {
|
|
814
|
+
record.splice(i, 1);
|
|
781
815
|
}
|
|
782
816
|
}
|
|
783
817
|
}
|
|
@@ -399,6 +399,9 @@
|
|
|
399
399
|
},
|
|
400
400
|
{
|
|
401
401
|
"event": "account deactivated",
|
|
402
|
+
"isChurnEvent": true,
|
|
403
|
+
"returnLikelihood": 0.15,
|
|
404
|
+
"isStrictEvent": true,
|
|
402
405
|
"properties": {
|
|
403
406
|
"reason": [
|
|
404
407
|
"low_sales",
|
|
@@ -410,48 +413,6 @@
|
|
|
410
413
|
}
|
|
411
414
|
}
|
|
412
415
|
],
|
|
413
|
-
"superProps": {
|
|
414
|
-
"platform": [
|
|
415
|
-
"ios",
|
|
416
|
-
"android",
|
|
417
|
-
"web"
|
|
418
|
-
],
|
|
419
|
-
"category": [
|
|
420
|
-
"electronics",
|
|
421
|
-
"clothing",
|
|
422
|
-
"home_garden",
|
|
423
|
-
"collectibles",
|
|
424
|
-
"sports",
|
|
425
|
-
"toys",
|
|
426
|
-
"automotive"
|
|
427
|
-
]
|
|
428
|
-
},
|
|
429
|
-
"userProps": {
|
|
430
|
-
"user_type": [
|
|
431
|
-
"buyer"
|
|
432
|
-
],
|
|
433
|
-
"segment": [
|
|
434
|
-
"window_shopper"
|
|
435
|
-
],
|
|
436
|
-
"seller_rating": {
|
|
437
|
-
"$range": [
|
|
438
|
-
1,
|
|
439
|
-
4
|
|
440
|
-
]
|
|
441
|
-
},
|
|
442
|
-
"total_transactions": [
|
|
443
|
-
0
|
|
444
|
-
],
|
|
445
|
-
"response_time_hours": {
|
|
446
|
-
"$range": [
|
|
447
|
-
7,
|
|
448
|
-
38
|
|
449
|
-
]
|
|
450
|
-
},
|
|
451
|
-
"store_name": [
|
|
452
|
-
"none"
|
|
453
|
-
]
|
|
454
|
-
},
|
|
455
416
|
"funnels": [
|
|
456
417
|
{
|
|
457
418
|
"sequence": [
|
|
@@ -460,6 +421,7 @@
|
|
|
460
421
|
"item viewed",
|
|
461
422
|
"add to cart"
|
|
462
423
|
],
|
|
424
|
+
"name": "Buyer Onboarding",
|
|
463
425
|
"isFirstFunnel": true,
|
|
464
426
|
"conversionRate": 40,
|
|
465
427
|
"timeToConvert": 72,
|
|
@@ -472,6 +434,7 @@
|
|
|
472
434
|
"add to cart",
|
|
473
435
|
"purchase completed"
|
|
474
436
|
],
|
|
437
|
+
"name": "Browse to Purchase",
|
|
475
438
|
"conversionRate": 30,
|
|
476
439
|
"timeToConvert": 48,
|
|
477
440
|
"weight": 5
|
|
@@ -483,6 +446,7 @@
|
|
|
483
446
|
"offer received",
|
|
484
447
|
"offer accepted"
|
|
485
448
|
],
|
|
449
|
+
"name": "Seller Listing Flow",
|
|
486
450
|
"conversionRate": 25,
|
|
487
451
|
"timeToConvert": 168,
|
|
488
452
|
"weight": 3
|
|
@@ -493,6 +457,7 @@
|
|
|
493
457
|
"message sent",
|
|
494
458
|
"offer accepted"
|
|
495
459
|
],
|
|
460
|
+
"name": "Offer Negotiation",
|
|
496
461
|
"conversionRate": 35,
|
|
497
462
|
"timeToConvert": 72,
|
|
498
463
|
"weight": 2
|
|
@@ -503,9 +468,66 @@
|
|
|
503
468
|
"shipping updated",
|
|
504
469
|
"review submitted"
|
|
505
470
|
],
|
|
471
|
+
"name": "Review After Purchase",
|
|
506
472
|
"conversionRate": 20,
|
|
507
473
|
"timeToConvert": 336,
|
|
508
474
|
"weight": 2
|
|
509
475
|
}
|
|
510
|
-
]
|
|
476
|
+
],
|
|
477
|
+
"superProps": {
|
|
478
|
+
"Platform": [
|
|
479
|
+
"ios",
|
|
480
|
+
"android",
|
|
481
|
+
"web"
|
|
482
|
+
],
|
|
483
|
+
"category": [
|
|
484
|
+
"electronics",
|
|
485
|
+
"clothing",
|
|
486
|
+
"home_garden",
|
|
487
|
+
"collectibles",
|
|
488
|
+
"sports",
|
|
489
|
+
"toys",
|
|
490
|
+
"automotive"
|
|
491
|
+
]
|
|
492
|
+
},
|
|
493
|
+
"userProps": {
|
|
494
|
+
"user_type": [
|
|
495
|
+
"buyer"
|
|
496
|
+
],
|
|
497
|
+
"segment": [
|
|
498
|
+
"window_shopper"
|
|
499
|
+
],
|
|
500
|
+
"seller_rating": {
|
|
501
|
+
"$range": [
|
|
502
|
+
1,
|
|
503
|
+
4
|
|
504
|
+
]
|
|
505
|
+
},
|
|
506
|
+
"total_transactions": [
|
|
507
|
+
0
|
|
508
|
+
],
|
|
509
|
+
"response_time_hours": {
|
|
510
|
+
"$range": [
|
|
511
|
+
7,
|
|
512
|
+
38
|
|
513
|
+
]
|
|
514
|
+
},
|
|
515
|
+
"store_name": [
|
|
516
|
+
"none"
|
|
517
|
+
]
|
|
518
|
+
},
|
|
519
|
+
"scdProps": {
|
|
520
|
+
"seller_tier": {
|
|
521
|
+
"type": "user",
|
|
522
|
+
"frequency": "month",
|
|
523
|
+
"timing": "fuzzy",
|
|
524
|
+
"max": 8,
|
|
525
|
+
"values": [
|
|
526
|
+
"new",
|
|
527
|
+
"verified",
|
|
528
|
+
"power",
|
|
529
|
+
"featured"
|
|
530
|
+
]
|
|
531
|
+
}
|
|
532
|
+
}
|
|
511
533
|
}
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
+
// ── TWEAK THESE ──
|
|
2
|
+
const SEED = "dm4-marketplace";
|
|
3
|
+
const num_days = 100;
|
|
4
|
+
const num_users = 5_000;
|
|
5
|
+
const avg_events_per_user = 120;
|
|
6
|
+
let token = "your-mixpanel-token";
|
|
7
|
+
|
|
8
|
+
// ── env overrides ──
|
|
9
|
+
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
10
|
+
|
|
1
11
|
import dayjs from "dayjs";
|
|
2
12
|
import utc from "dayjs/plugin/utc.js";
|
|
3
13
|
import "dotenv/config";
|
|
4
14
|
import * as u from "../../lib/utils/utils.js";
|
|
5
15
|
import * as v from "ak-tools";
|
|
6
16
|
|
|
7
|
-
const SEED = "dm4-marketplace";
|
|
8
17
|
dayjs.extend(utc);
|
|
9
18
|
const chance = u.initChance(SEED);
|
|
10
|
-
const num_users = 5_000;
|
|
11
|
-
const days = 100;
|
|
12
19
|
const NOW = dayjs();
|
|
13
|
-
const DATASET_START = NOW.subtract(
|
|
20
|
+
const DATASET_START = NOW.subtract(num_days, "days");
|
|
14
21
|
|
|
15
22
|
/** @typedef {import("../../types").Dungeon} Config */
|
|
16
23
|
|
|
@@ -213,12 +220,12 @@ const listingIds = v.range(1, 500).map(() => `LST_${v.uid(8)}`);
|
|
|
213
220
|
* high transaction volumes, and near-perfect ratings.
|
|
214
221
|
*
|
|
215
222
|
* ───────────────────────────────────────────────────────────────
|
|
216
|
-
* 8. FREQUENT BUYER FUNNEL LIFT (
|
|
223
|
+
* 8. FREQUENT BUYER FUNNEL LIFT (everything hook)
|
|
217
224
|
* ───────────────────────────────────────────────────────────────
|
|
218
225
|
*
|
|
219
|
-
* PATTERN:
|
|
220
|
-
*
|
|
221
|
-
*
|
|
226
|
+
* PATTERN: Non-frequent-buyer users lose ~25% of "purchase completed"
|
|
227
|
+
* events (last step of Browse to Purchase funnel), simulating lower
|
|
228
|
+
* conversion. Frequent buyers retain all purchase events.
|
|
222
229
|
*
|
|
223
230
|
* HOW TO FIND IT IN MIXPANEL:
|
|
224
231
|
*
|
|
@@ -249,10 +256,10 @@ const listingIds = v.range(1, 500).map(() => `LST_${v.uid(8)}`);
|
|
|
249
256
|
|
|
250
257
|
/** @type {Config} */
|
|
251
258
|
const config = {
|
|
252
|
-
token
|
|
259
|
+
token,
|
|
253
260
|
seed: SEED,
|
|
254
|
-
numDays:
|
|
255
|
-
numEvents: num_users *
|
|
261
|
+
numDays: num_days,
|
|
262
|
+
numEvents: num_users * avg_events_per_user,
|
|
256
263
|
numUsers: num_users,
|
|
257
264
|
hasAnonIds: false,
|
|
258
265
|
hasSessionIds: true,
|
|
@@ -269,10 +276,16 @@ const config = {
|
|
|
269
276
|
hasAdSpend: false,
|
|
270
277
|
percentUsersBornInDataset: 35,
|
|
271
278
|
hasAvatar: true,
|
|
272
|
-
batchSize: 2_500_000,
|
|
273
279
|
concurrency: 1,
|
|
274
280
|
writeToDisk: false,
|
|
275
|
-
scdProps: {
|
|
281
|
+
scdProps: {
|
|
282
|
+
seller_tier: {
|
|
283
|
+
values: ["new", "verified", "power", "featured"],
|
|
284
|
+
frequency: "month",
|
|
285
|
+
timing: "fuzzy",
|
|
286
|
+
max: 8
|
|
287
|
+
}
|
|
288
|
+
},
|
|
276
289
|
mirrorProps: {},
|
|
277
290
|
lookupTables: [],
|
|
278
291
|
|
|
@@ -496,7 +509,7 @@ const config = {
|
|
|
496
509
|
|
|
497
510
|
// ── SuperProps ──────────────────────────────────────────
|
|
498
511
|
superProps: {
|
|
499
|
-
|
|
512
|
+
Platform: ["ios", "android", "web"],
|
|
500
513
|
category: ["electronics", "clothing", "home_garden", "collectibles", "sports", "toys", "automotive"],
|
|
501
514
|
},
|
|
502
515
|
|
|
@@ -508,6 +521,8 @@ const config = {
|
|
|
508
521
|
total_transactions: [0],
|
|
509
522
|
response_time_hours: u.weighNumRange(0, 48, 0.3),
|
|
510
523
|
store_name: ["none"],
|
|
524
|
+
Platform: ["ios", "android", "web"],
|
|
525
|
+
category: ["electronics", "clothing", "home_garden", "collectibles", "sports", "toys", "automotive"],
|
|
511
526
|
},
|
|
512
527
|
|
|
513
528
|
// ── Personas ──────────────────────────────────
|
|
@@ -715,13 +730,9 @@ const config = {
|
|
|
715
730
|
}
|
|
716
731
|
|
|
717
732
|
// ── HOOK 8: FREQUENT BUYER FUNNEL LIFT (funnel-pre) ──
|
|
718
|
-
//
|
|
733
|
+
// (conversionRate filtering moved to everything hook)
|
|
719
734
|
if (type === "funnel-pre") {
|
|
720
|
-
|
|
721
|
-
if (meta.profile.segment === "frequent_buyer") {
|
|
722
|
-
record.conversionRate = Math.min(record.conversionRate * 1.3, 90);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
735
|
+
// no-op: conversion filtering handled via event dropping in everything hook
|
|
725
736
|
}
|
|
726
737
|
|
|
727
738
|
// ── HOOK 1: FEE CHANGE IMPACT (event) ────────────────
|
|
@@ -749,11 +760,31 @@ const config = {
|
|
|
749
760
|
|
|
750
761
|
// ── EVERYTHING HOOKS ─────────────────────────────────
|
|
751
762
|
if (type === "everything") {
|
|
752
|
-
|
|
763
|
+
let events = record;
|
|
753
764
|
if (!events.length) return record;
|
|
754
765
|
|
|
755
766
|
const profile = meta.profile;
|
|
756
767
|
|
|
768
|
+
// ── SUPERPROP STAMPING ──────────────────────────
|
|
769
|
+
// Stamp superProps from profile so they are consistent per user.
|
|
770
|
+
if (profile) {
|
|
771
|
+
events.forEach(e => {
|
|
772
|
+
if (profile.Platform) e.Platform = profile.Platform;
|
|
773
|
+
if (profile.category) e.category = profile.category;
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// ── HOOK 8: FREQUENT BUYER CONVERSION FILTER ────
|
|
778
|
+
// Non-frequent-buyer users drop ~25% of "purchase completed"
|
|
779
|
+
// (last step of Browse to Purchase funnel) to simulate lower conversion.
|
|
780
|
+
if (profile && profile.segment !== "frequent_buyer") {
|
|
781
|
+
record = record.filter(e => {
|
|
782
|
+
if (e.event === "purchase completed" && chance.bool({ likelihood: 25 })) return false;
|
|
783
|
+
return true;
|
|
784
|
+
});
|
|
785
|
+
events = record;
|
|
786
|
+
}
|
|
787
|
+
|
|
757
788
|
// ── HOOK 3: SELLER SUCCESS → BUYER TRUST ─────────
|
|
758
789
|
// Power sellers get 2x purchase events (cloned from existing).
|
|
759
790
|
if (profile && profile.segment === "power_seller") {
|