@ak--47/dungeon-master 1.3.0 → 1.4.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dungeons/technical/array-of-object-lookup.js +0 -2
  3. package/dungeons/technical/hook-helpers-verify.js +89 -0
  4. package/dungeons/technical/identity-model-verify.js +47 -0
  5. package/dungeons/technical/pattern-aggregate-by-bin.js +41 -0
  6. package/dungeons/technical/pattern-attributed-by-source.js +42 -0
  7. package/dungeons/technical/pattern-frequency-by-frequency.js +40 -0
  8. package/dungeons/technical/pattern-funnel-frequency.js +54 -0
  9. package/dungeons/technical/pattern-ttc-by-segment.js +45 -0
  10. package/dungeons/technical/simple.js +3 -4
  11. package/dungeons/technical/simplest-schema.json +5 -0
  12. package/dungeons/technical/text-generation.js +1 -1
  13. package/dungeons/vertical/ai-platform.js +216 -164
  14. package/dungeons/vertical/community.js +74 -41
  15. package/dungeons/vertical/crypto.js +343 -233
  16. package/dungeons/vertical/dating.js +246 -292
  17. package/dungeons/vertical/devtools.js +179 -70
  18. package/dungeons/vertical/ecommerce.js +275 -122
  19. package/dungeons/vertical/education.js +345 -312
  20. package/dungeons/vertical/fintech.js +445 -314
  21. package/dungeons/vertical/fitness.js +199 -165
  22. package/dungeons/vertical/food-delivery.js +309 -330
  23. package/dungeons/vertical/gaming.js +387 -241
  24. package/dungeons/vertical/healthcare.js +149 -66
  25. package/dungeons/vertical/insurance-application.js +173 -75
  26. package/dungeons/vertical/logistics.js +107 -12
  27. package/dungeons/vertical/marketplace.js +171 -76
  28. package/dungeons/vertical/media.js +259 -402
  29. package/dungeons/vertical/real-estate.js +336 -318
  30. package/dungeons/vertical/sass.js +294 -288
  31. package/dungeons/vertical/social.js +289 -234
  32. package/dungeons/vertical/travel.js +66 -20
  33. package/index.js +17 -17
  34. package/lib/core/config-validator.js +226 -175
  35. package/lib/core/context.js +10 -24
  36. package/lib/core/storage.js +6 -1
  37. package/lib/generators/events.js +57 -104
  38. package/lib/generators/funnels.js +212 -93
  39. package/lib/generators/mirror.js +3 -2
  40. package/lib/generators/product-names.js +1 -1
  41. package/lib/generators/scd.js +2 -1
  42. package/lib/generators/text.js +1 -1
  43. package/lib/hook-helpers/_internal.js +23 -0
  44. package/lib/hook-helpers/cohort.js +124 -0
  45. package/lib/hook-helpers/identity.js +56 -0
  46. package/lib/hook-helpers/index.js +44 -0
  47. package/lib/hook-helpers/inject.js +99 -0
  48. package/lib/hook-helpers/mutate.js +151 -0
  49. package/lib/hook-helpers/timing.js +99 -0
  50. package/lib/hook-patterns/aggregate-per-user-by-bin.js +38 -0
  51. package/lib/hook-patterns/attributed-by-source.js +72 -0
  52. package/lib/hook-patterns/frequency-by-frequency.js +46 -0
  53. package/lib/hook-patterns/funnel-frequency-breakdown.js +73 -0
  54. package/lib/hook-patterns/index.js +14 -0
  55. package/lib/hook-patterns/time-to-convert-by-segment.js +41 -0
  56. package/lib/orchestrators/user-loop.js +143 -293
  57. package/lib/utils/utils.js +97 -52
  58. package/lib/verify/emulate-breakdown.js +281 -0
  59. package/lib/verify/index.js +12 -0
  60. package/lib/verify/verify-dungeon.js +61 -0
  61. package/package.json +6 -4
  62. package/scripts/smoke-test-all.mjs +162 -0
  63. package/scripts/verify-runner.mjs +72 -24
  64. package/types.d.ts +434 -225
  65. package/dungeons/technical/ad-spend-schema.json +0 -128
  66. package/dungeons/technical/anonymous-users-schema.json +0 -92
  67. package/dungeons/technical/array-of-object-lookup-schema.json +0 -191
  68. package/dungeons/technical/experiments-schema.json +0 -203
  69. package/dungeons/technical/foobar-schema.json +0 -362
  70. package/dungeons/technical/group-analytics-schema.json +0 -241
  71. package/dungeons/technical/mirror-strategies-schema.json +0 -84
  72. package/dungeons/technical/nested-objects-schema.json +0 -145
  73. package/dungeons/technical/retention-cadence-schema.json +0 -37
  74. package/dungeons/technical/sanity-schema.json +0 -185
  75. package/dungeons/technical/scale-test-schema.json +0 -70
  76. package/dungeons/technical/scd-schema.json +0 -467
  77. package/dungeons/technical/simple-schema.json +0 -362
  78. package/dungeons/technical/text-generation-schema.json +0 -1062
  79. package/dungeons/vertical/ai-platform-schema.json +0 -617
  80. package/dungeons/vertical/community-schema.json +0 -579
  81. package/dungeons/vertical/crypto-schema.json +0 -546
  82. package/dungeons/vertical/dating-schema.json +0 -401
  83. package/dungeons/vertical/devtools-schema.json +0 -601
  84. package/dungeons/vertical/ecommerce-schema.json +0 -604
  85. package/dungeons/vertical/education-schema.json +0 -5686
  86. package/dungeons/vertical/fintech-schema.json +0 -630
  87. package/dungeons/vertical/fitness-schema.json +0 -530
  88. package/dungeons/vertical/food-delivery-schema.json +0 -36728
  89. package/dungeons/vertical/gaming-schema.json +0 -2703
  90. package/dungeons/vertical/healthcare-schema.json +0 -549
  91. package/dungeons/vertical/insurance-application-schema.json +0 -485
  92. package/dungeons/vertical/logistics-schema.json +0 -574
  93. package/dungeons/vertical/marketplace-schema.json +0 -533
  94. package/dungeons/vertical/media-schema.json +0 -4749
  95. package/dungeons/vertical/real-estate-schema.json +0 -527
  96. package/dungeons/vertical/sass-schema.json +0 -3128
  97. package/dungeons/vertical/social-schema.json +0 -620
  98. package/dungeons/vertical/travel-schema.json +0 -580
@@ -1,7 +1,7 @@
1
1
  // ── TWEAK THESE ──
2
2
  const SEED = "dm4-community";
3
- const num_days = 100;
4
- const num_users = 5_000;
3
+ const num_days = 120;
4
+ const num_users = 10_000;
5
5
  const avg_events_per_user_per_day = 1.2;
6
6
  let token = "your-mixpanel-token";
7
7
 
@@ -16,9 +16,6 @@ import * as v from "ak-tools";
16
16
 
17
17
  dayjs.extend(utc);
18
18
  const chance = u.initChance(SEED);
19
- const NOW = dayjs();
20
- const DATASET_START = NOW.subtract(num_days, "days");
21
-
22
19
  /** @typedef {import("../../types").Dungeon} Config */
23
20
 
24
21
  // Generate consistent wiki/article IDs at module level
@@ -34,7 +31,7 @@ const communityIds = v.range(1, 30).map(() => `COMM_${v.uid(4)}`);
34
31
  * users create articles, discuss topics, moderate content, and
35
32
  * build collaborative knowledge bases across fandoms.
36
33
  *
37
- * - 5,000 users over 100 days, ~600K events
34
+ * - 10,000 users over 120 days
38
35
  * - Multi-role system: power creators (5%), moderators (8%),
39
36
  * active contributors (25%), readers (45%), lurkers (17%)
40
37
  * - Core loop: sign up -> search -> read articles -> contribute -> discuss
@@ -56,7 +53,16 @@ const communityIds = v.range(1, 30).map(() => `COMM_${v.uid(4)}`);
56
53
 
57
54
  /**
58
55
  * ===================================================================
59
- * ANALYTICS HOOKS (8 hooks)
56
+ * ANALYTICS HOOKS (10 hooks)
57
+ *
58
+ * NOTE: All cohort effects are HIDDEN — no flag stamping. Discoverable via
59
+ * raw-prop breakdowns (day, content_hub, subscription_tier) or behavioral cohorts.
60
+ *
61
+ * Adds 9. CONTENT CREATION TIME-TO-CONVERT (Pro 1.3x faster, Free 1.25x slower)
62
+ * [funnel-post: visible only in Mixpanel funnel median TTC; cross-event
63
+ * MIN→MIN SQL queries do NOT show this]
64
+ * and 10. ARTICLE-PUBLISHED MAGIC NUMBER (sweet 2-5 → +35% upvote_count;
65
+ * over 6+ → drop 25% upvote events).
60
66
  * ===================================================================
61
67
  *
62
68
  * -------------------------------------------------------------------
@@ -73,9 +79,9 @@ const communityIds = v.range(1, 30).map(() => `COMM_${v.uid(4)}`);
73
79
  * - Report type: Insights
74
80
  * - Event: "article published"
75
81
  * - Measure: Average of "word_count"
76
- * - Breakdown: "is_weekend"
77
- * - Expected: is_weekend=true should show ~1.5x avg word_count vs false
78
- * (true ~ 2250, false ~ 1500)
82
+ * - Breakdown: Day of Week
83
+ * - Expected: Sat/Sun ~ 1.5x avg word_count vs weekdays
84
+ * (weekend ~ 3325, weekday ~ 2218)
79
85
  *
80
86
  * REAL-WORLD ANALOGUE: Community wikis see longer, more thoughtful
81
87
  * contributions on weekends when creators have uninterrupted time.
@@ -256,12 +262,16 @@ const communityIds = v.range(1, 30).map(() => `COMM_${v.uid(4)}`);
256
262
 
257
263
  /** @type {Config} */
258
264
  const config = {
265
+ version: 2,
259
266
  token,
260
267
  seed: SEED,
261
- numDays: num_days,
268
+ datasetStart: "2026-01-01T00:00:00Z",
269
+ datasetEnd: "2026-05-01T23:59:59Z",
270
+ // numDays: num_days,
262
271
  avgEventsPerUserPerDay: avg_events_per_user_per_day,
263
272
  numUsers: num_users,
264
- hasAnonIds: false,
273
+ hasAnonIds: true,
274
+ avgDevicePerUser: 2,
265
275
  hasSessionIds: true,
266
276
  format: "json",
267
277
  gzip: true,
@@ -294,6 +304,7 @@ const config = {
294
304
  event: "account created",
295
305
  weight: 1,
296
306
  isFirstEvent: true,
307
+ isAuthEvent: true,
297
308
  properties: {
298
309
  referral_source: ["organic", "google_search", "reddit_referral", "youtube_link", "friend_invite"],
299
310
  },
@@ -306,7 +317,6 @@ const config = {
306
317
  content_hub: ["gaming", "anime", "movies", "tv", "comics", "music"],
307
318
  view_count: u.weighNumRange(1, 100, 0.4, 50),
308
319
  time_on_page_sec: u.weighNumRange(5, 600, 0.4, 45),
309
- is_weekend: [false],
310
320
  },
311
321
  },
312
322
  {
@@ -318,8 +328,6 @@ const config = {
318
328
  word_count: u.weighNumRange(200, 5000, 0.4, 1500),
319
329
  has_images: [true, true, true, false],
320
330
  category: ["lore", "character", "episode_guide", "review", "tutorial", "news"],
321
- is_weekend: [false],
322
- poll_enabled: [false],
323
331
  },
324
332
  },
325
333
  {
@@ -613,7 +621,7 @@ const config = {
613
621
  name: "google_search",
614
622
  source: "google",
615
623
  medium: "organic",
616
- activeDays: [0, 100],
624
+ activeDays: [0, 120],
617
625
  dailyBudget: [100, 400],
618
626
  acquisitionRate: 0.03,
619
627
  userPersonaBias: { reader: 0.5, lurker: 0.3 },
@@ -622,7 +630,7 @@ const config = {
622
630
  name: "reddit_referral",
623
631
  source: "reddit",
624
632
  medium: "referral",
625
- activeDays: [0, 100],
633
+ activeDays: [0, 120],
626
634
  dailyBudget: [50, 200],
627
635
  acquisitionRate: 0.02,
628
636
  userPersonaBias: { active_contributor: 0.5, moderator: 0.3 },
@@ -631,7 +639,7 @@ const config = {
631
639
  name: "youtube_link",
632
640
  source: "youtube",
633
641
  medium: "referral",
634
- activeDays: [0, 100],
642
+ activeDays: [0, 120],
635
643
  dailyBudget: [75, 250],
636
644
  acquisitionRate: 0.02,
637
645
  userPersonaBias: { lurker: 0.5, reader: 0.3 },
@@ -694,20 +702,30 @@ const config = {
694
702
  }
695
703
  }
696
704
 
697
- // -- HOOK 8: PRO SUBSCRIBER CONTENT CREATION LIFT
698
- // Conversion differences handled in everything hook via event filtering.
699
- // (funnel-pre conversionRate modifications are diluted by organic events)
700
-
701
- // -- HOOK 1: WEEKEND CONTENT SURGE ----------------------------
702
- // Moved to everything hook (after sessionization) so DOW tags
703
- // match final timestamps. See everything hook below.
704
-
705
- // -- HOOK 2: TRENDING TOPIC WINDOW ----------------------------
706
- // Moved to everything hook (after superProp stamping) so it
707
- // uses the profile's consistent content_hub.
705
+ // HOOK 9 (T2C): CONTENT CREATION TIME-TO-CONVERT (funnel-post)
706
+ // Pro/supporter subscribers complete the Content Creation funnel
707
+ // 1.3x faster (factor 0.77); Free 1.25x slower (factor 1.25).
708
+ if (type === "funnel-post") {
709
+ const segment = meta?.profile?.subscription_tier;
710
+ if (Array.isArray(record) && record.length > 1) {
711
+ const factor = (
712
+ segment === "pro" || segment === "supporter" ? 0.77 :
713
+ segment === "free" ? 1.25 :
714
+ 1.0
715
+ );
716
+ if (factor !== 1.0) {
717
+ for (let i = 1; i < record.length; i++) {
718
+ const prev = dayjs(record[i - 1].time);
719
+ const newGap = Math.round(dayjs(record[i].time).diff(prev) * factor);
720
+ record[i].time = prev.add(newGap, "milliseconds").toISOString();
721
+ }
722
+ }
723
+ }
724
+ }
708
725
 
709
726
  // -- EVERYTHING HOOKS -----------------------------------------
710
727
  if (type === "everything") {
728
+ const datasetStart = dayjs.unix(meta.datasetStart);
711
729
  let events = record;
712
730
  if (!events.length) return record;
713
731
  const profile = meta && meta.profile ? meta.profile : {};
@@ -721,17 +739,13 @@ const config = {
721
739
  if (profile.content_hub) e.content_hub = profile.content_hub;
722
740
  });
723
741
 
724
- // -- HOOK 1: WEEKEND CONTENT SURGE -------------------------
725
- // Articles published/viewed on weekends get 1.5x word_count.
726
- // Runs after sessionization so DOW tags match final timestamps.
742
+ // HOOK 1: WEEKEND CONTENT SURGE — articles on Sat/Sun get
743
+ // word_count 1.5x. Mutates raw prop. No flag.
727
744
  for (const e of events) {
728
745
  if (e.event === 'article published' || e.event === 'article viewed') {
729
746
  const dow = new Date(e.time).getUTCDay();
730
- if (dow === 0 || dow === 6) {
731
- e.is_weekend = true;
732
- if (e.word_count) {
733
- e.word_count = Math.floor(e.word_count * 1.5);
734
- }
747
+ if ((dow === 0 || dow === 6) && e.word_count) {
748
+ e.word_count = Math.floor(e.word_count * 1.5);
735
749
  }
736
750
  }
737
751
  }
@@ -740,8 +754,8 @@ const config = {
740
754
  // Days 35-50: gaming hub articles get 2x view_count.
741
755
  // Runs after superProp stamping so content_hub is the
742
756
  // profile's consistent value, not the random event-level one.
743
- const TREND_START = DATASET_START.add(35, "days");
744
- const TREND_END = DATASET_START.add(50, "days");
757
+ const TREND_START = datasetStart.add(35, "days");
758
+ const TREND_END = datasetStart.add(50, "days");
745
759
  if (profile.content_hub === "gaming") {
746
760
  events.forEach(e => {
747
761
  if (e.event === "article viewed") {
@@ -807,8 +821,8 @@ const config = {
807
821
  });
808
822
  }
809
823
 
810
- // -- HOOK 6: LURKER CHURN ---------------------------------
811
- // Users with <5 total events lose 60% after day 10.
824
+ // HOOK 6: LURKER CHURN — users with <5 events lose 60% after
825
+ // day 10. No flag.
812
826
  if (events.length < 5 && events.length > 0) {
813
827
  const firstEventTime = dayjs(events[0].time);
814
828
  const cutoff = firstEventTime.add(10, "days");
@@ -819,6 +833,25 @@ const config = {
819
833
  }
820
834
  }
821
835
 
836
+ // HOOK 10: ARTICLE-PUBLISHED MAGIC NUMBER (no flags)
837
+ // Sweet 2-5 articles published → +35% on upvote_count for
838
+ // upvote-given events. Over 6+ → drop 25% of upvote-given events
839
+ // (over-publishing dilutes signal). No flag.
840
+ const articleCount = events.filter(e => e.event === "article published").length;
841
+ if (articleCount >= 2 && articleCount <= 5) {
842
+ events.forEach(e => {
843
+ if (e.event === "upvote given" && typeof e.upvote_count === "number") {
844
+ e.upvote_count = Math.round(e.upvote_count * 1.35);
845
+ }
846
+ });
847
+ } else if (articleCount >= 6) {
848
+ for (let i = events.length - 1; i >= 0; i--) {
849
+ if (events[i].event === "upvote given" && chance.bool({ likelihood: 25 })) {
850
+ events.splice(i, 1);
851
+ }
852
+ }
853
+ }
854
+
822
855
  return events;
823
856
  }
824
857