@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.
Files changed (72) hide show
  1. package/dungeons/technical/ad-spend-schema.json +2 -2
  2. package/dungeons/technical/ad-spend.js +16 -5
  3. package/dungeons/technical/anonymous-users-schema.json +4 -3
  4. package/dungeons/technical/anonymous-users.js +16 -6
  5. package/dungeons/technical/array-of-object-lookup-schema.json +1 -1
  6. package/dungeons/technical/array-of-object-lookup.js +22 -6
  7. package/dungeons/technical/experiments-schema.json +25 -19
  8. package/dungeons/technical/experiments.js +24 -6
  9. package/dungeons/technical/foobar.js +29 -13
  10. package/dungeons/technical/group-analytics-schema.json +92 -0
  11. package/dungeons/technical/group-analytics.js +16 -6
  12. package/dungeons/technical/mirror-strategies-schema.json +4 -4
  13. package/dungeons/technical/mirror-strategies.js +16 -6
  14. package/dungeons/technical/nested-objects.js +16 -5
  15. package/dungeons/technical/retention-cadence.js +14 -9
  16. package/dungeons/technical/sanity-schema.json +22 -22
  17. package/dungeons/technical/sanity.js +26 -6
  18. package/dungeons/technical/scale-test.js +16 -5
  19. package/dungeons/technical/scd-schema.json +230 -33
  20. package/dungeons/technical/scd.js +25 -5
  21. package/dungeons/technical/simple-schema.json +132 -48
  22. package/dungeons/technical/simple.js +21 -6
  23. package/dungeons/technical/simplest-schema.json +168 -79
  24. package/dungeons/technical/simplest.js +24 -7
  25. package/dungeons/technical/text-generation-schema.json +7 -4
  26. package/dungeons/technical/text-generation.js +23 -8
  27. package/dungeons/user/.gitkeep +0 -0
  28. package/dungeons/user/shalini.js +847 -0
  29. package/dungeons/vertical/community-schema.json +71 -49
  30. package/dungeons/vertical/community.js +57 -30
  31. package/dungeons/vertical/devtools-schema.json +69 -48
  32. package/dungeons/vertical/devtools.js +58 -31
  33. package/dungeons/vertical/ecommerce-schema.json +126 -108
  34. package/dungeons/vertical/ecommerce.js +32 -8
  35. package/dungeons/vertical/education-schema.json +5261 -272
  36. package/dungeons/vertical/education.js +45 -22
  37. package/dungeons/vertical/fintech-schema.json +165 -98
  38. package/dungeons/vertical/fintech.js +43 -11
  39. package/dungeons/vertical/fitness-schema.json +69 -47
  40. package/dungeons/vertical/fitness.js +56 -24
  41. package/dungeons/vertical/food-delivery-schema.json +36237 -187
  42. package/dungeons/vertical/food-delivery.js +67 -15
  43. package/dungeons/vertical/gaming-schema.json +104 -60
  44. package/dungeons/vertical/gaming.js +61 -16
  45. package/dungeons/vertical/healthcare-schema.json +70 -48
  46. package/dungeons/vertical/healthcare.js +77 -42
  47. package/dungeons/vertical/insurance-application-schema.json +66 -47
  48. package/dungeons/vertical/insurance-application.js +34 -10
  49. package/dungeons/vertical/logistics-schema.json +60 -38
  50. package/dungeons/vertical/logistics.js +77 -43
  51. package/dungeons/vertical/marketplace-schema.json +65 -43
  52. package/dungeons/vertical/marketplace.js +52 -21
  53. package/dungeons/vertical/media-schema.json +4085 -123
  54. package/dungeons/vertical/media.js +65 -18
  55. package/dungeons/vertical/rpg-schema.json +1984 -147
  56. package/dungeons/vertical/rpg.js +47 -11
  57. package/dungeons/vertical/sass-schema.json +2622 -147
  58. package/dungeons/vertical/sass.js +40 -10
  59. package/dungeons/vertical/social-schema.json +146 -78
  60. package/dungeons/vertical/social.js +39 -10
  61. package/dungeons/vertical/travel-schema.json +57 -35
  62. package/dungeons/vertical/travel.js +58 -21
  63. package/index.js +4 -1
  64. package/lib/core/config-validator.js +13 -2
  65. package/lib/core/context.js +9 -0
  66. package/lib/core/storage.js +2 -0
  67. package/lib/generators/events.js +3 -5
  68. package/lib/orchestrators/user-loop.js +24 -1
  69. package/lib/utils/utils.js +129 -9
  70. package/package.json +1 -1
  71. package/scripts/extract-dungeon-schema.mjs +64 -18
  72. package/types.d.ts +8 -3
@@ -1,16 +1,23 @@
1
+ // ── TWEAK THESE ──
2
+ const SEED = "harness-media";
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 = "harness-media";
8
17
  dayjs.extend(utc);
9
18
  const chance = u.initChance(SEED);
10
- const num_users = 5_000;
11
- const days = 100;
12
19
 
13
- /** @typedef {import("../types.js").Dungeon} Config */
20
+ /** @typedef {import("../../types").Dungeon} Config */
14
21
 
15
22
  /**
16
23
  * ═══════════════════════════════════════════════════════════════════════════════
@@ -218,10 +225,10 @@ const blockbusterId = `blockbuster_${v.uid(8)}`;
218
225
 
219
226
  /** @type {Config} */
220
227
  const config = {
221
- token: "",
228
+ token,
222
229
  seed: SEED,
223
- numDays: days,
224
- numEvents: num_users * 120,
230
+ numDays: num_days,
231
+ numEvents: num_users * avg_events_per_user,
225
232
  numUsers: num_users,
226
233
  hasAnonIds: false,
227
234
  hasSessionIds: true,
@@ -238,7 +245,6 @@ const config = {
238
245
  hasAdSpend: false,
239
246
  percentUsersBornInDataset: 50,
240
247
  hasAvatar: true,
241
- batchSize: 2_500_000,
242
248
  concurrency: 1,
243
249
  writeToDisk: false,
244
250
 
@@ -541,7 +547,14 @@ const config = {
541
547
  device_type: ["smart_tv", "mobile", "tablet", "laptop", "desktop"],
542
548
  },
543
549
 
544
- scdProps: {},
550
+ scdProps: {
551
+ subscription_plan: {
552
+ values: ["free", "basic", "standard", "premium"],
553
+ frequency: "month",
554
+ timing: "fuzzy",
555
+ max: 6
556
+ }
557
+ },
545
558
 
546
559
  userProps: {
547
560
  "preferred_genre": ["action", "comedy", "drama", "documentary", "horror", "sci_fi", "animation"],
@@ -549,6 +562,8 @@ const config = {
549
562
  "total_watch_hours": u.weighNumRange(0, 500, 0.8, 50),
550
563
  "profiles_count": u.weighNumRange(1, 5),
551
564
  "downloads_enabled": [false, false, false, true, true],
565
+ "subscription_plan": ["free", "free", "standard", "standard", "standard", "premium"],
566
+ "device_type": ["smart_tv", "mobile", "tablet", "laptop", "desktop"],
552
567
  },
553
568
 
554
569
  lookupTables: [],
@@ -558,22 +573,22 @@ const config = {
558
573
  *
559
574
  * This hook function creates 8 deliberate patterns in the data:
560
575
  *
561
- * 1. GENRE FUNNEL CONVERSION: Comedy/Animation complete more; Documentary abandons more (funnel-pre)
576
+ * 1. GENRE FUNNEL CONVERSION: Comedy/Animation props in funnel-pre; documentary completion drop in everything
562
577
  * 2. BINGE-WATCHING: Users with 3+ consecutive completions get extra episodes (everything)
563
578
  * 3. WEEKEND vs WEEKDAY: Weekend sessions 1.5x longer; weekday prime-time tagging (event)
564
579
  * 4. AD FATIGUE CHURN: Free-tier users with 10+ ads churn after day 45 (everything)
565
580
  * 5. NEW RELEASE SPIKE: Blockbuster release on day 50 drives content selection (event)
566
581
  * 6. KIDS PROFILE SAFETY: Kids mode restricts genres and drops ads (event)
567
- * 7. RECOMMENDATION ENGINE IMPROVEMENT: Post-day-60 boost to engagement funnel (funnel-pre)
582
+ * 7. RECOMMENDATION ENGINE IMPROVEMENT: Props in funnel-pre; pre-day-60 content rated drop in everything
568
583
  * 8. SUBTITLE USERS WATCH MORE: Subtitle-enabled users have higher completion rates (everything)
569
584
  */
570
585
  hook: function (record, type, meta) {
571
586
  const NOW = dayjs();
572
- const DATASET_START = NOW.subtract(days, 'days');
587
+ const DATASET_START = NOW.subtract(num_days, 'days');
573
588
  // FIXED_START is the pre-shift time range start; needed because
574
589
  // meta.firstEventTime in funnel-pre is in the FIXED (pre-shift) timeframe
575
590
  const FIXED_NOW_UNIX = dayjs('2024-02-02').unix();
576
- const FIXED_START = dayjs.unix(FIXED_NOW_UNIX).subtract(days, 'days');
591
+ const FIXED_START = dayjs.unix(FIXED_NOW_UNIX).subtract(num_days, 'days');
577
592
 
578
593
  // ─────────────────────────────────────────────────────────────
579
594
  // Hook #1: GENRE FUNNEL CONVERSION (funnel-pre)
@@ -584,21 +599,17 @@ const config = {
584
599
  const genre = record.props.genre;
585
600
 
586
601
  if (genre === "comedy" || genre === "animation") {
587
- record.conversionRate = record.conversionRate * 1.3;
588
602
  record.props.genre_boost = true;
589
603
  record.props.genre_penalty = false;
590
604
  } else if (genre === "documentary") {
591
- record.conversionRate = record.conversionRate * 0.7;
592
605
  record.props.genre_boost = false;
593
606
  record.props.genre_penalty = true;
594
607
  } else if (!genre && chance.bool({ likelihood: 25 })) {
595
608
  // Randomly apply genre effects when genre isn't in funnel props
596
609
  if (chance.bool({ likelihood: 60 })) {
597
- record.conversionRate = record.conversionRate * 1.3;
598
610
  record.props.genre_boost = true;
599
611
  record.props.genre_penalty = false;
600
612
  } else {
601
- record.conversionRate = record.conversionRate * 0.7;
602
613
  record.props.genre_boost = false;
603
614
  record.props.genre_penalty = true;
604
615
  }
@@ -617,7 +628,6 @@ const config = {
617
628
  const isAfterImprovement = funnelTime && funnelTime.isAfter(FIXED_START.add(60, 'days'));
618
629
 
619
630
  if (isEngagementFunnel && isAfterImprovement) {
620
- record.conversionRate = record.conversionRate * 1.5;
621
631
  record.props.improved_recs = true;
622
632
  } else {
623
633
  record.props.improved_recs = false;
@@ -705,6 +715,43 @@ const config = {
705
715
  const userEvents = record;
706
716
  if (!userEvents || userEvents.length === 0) return record;
707
717
 
718
+ const profile = meta.profile;
719
+
720
+ // ─── Stamp superProps from profile (consistent per user) ───
721
+ const stampPlan = profile && profile.subscription_plan ? profile.subscription_plan : undefined;
722
+ const stampDevice = profile && profile.device_type ? profile.device_type : undefined;
723
+ if (stampPlan || stampDevice) {
724
+ userEvents.forEach(e => {
725
+ if (stampPlan) e.subscription_plan = stampPlan;
726
+ if (stampDevice) e.device_type = stampDevice;
727
+ });
728
+ }
729
+
730
+ // ─── Bug 2 fix: Genre funnel conversion filtering ───
731
+ // Drop ~25% of "playback completed" events for documentary genre
732
+ // to simulate lower documentary funnel completion (was conversionRate * 0.7)
733
+ for (let i = userEvents.length - 1; i >= 0; i--) {
734
+ const evt = userEvents[i];
735
+ if (evt.event === "playback completed" && evt.genre_penalty === true) {
736
+ if (chance.bool({ likelihood: 25 })) {
737
+ userEvents.splice(i, 1);
738
+ }
739
+ }
740
+ }
741
+
742
+ // ─── Bug 2 fix: Recommendation engine improvement filtering ───
743
+ // Drop ~30% of "content rated" events that occur BEFORE day 60
744
+ // to simulate lower pre-improvement rec engine conversion (was conversionRate * 1.5)
745
+ const IMPROVEMENT_DATE = DATASET_START.add(60, 'days');
746
+ for (let i = userEvents.length - 1; i >= 0; i--) {
747
+ const evt = userEvents[i];
748
+ if (evt.event === "content rated" && dayjs(evt.time).isBefore(IMPROVEMENT_DATE)) {
749
+ if (chance.bool({ likelihood: 30 })) {
750
+ userEvents.splice(i, 1);
751
+ }
752
+ }
753
+ }
754
+
708
755
  const firstEventTime = dayjs(userEvents[0].time);
709
756
 
710
757
  // ─── First pass: identify user patterns ───