@ak--47/dungeon-master 1.3.0 → 1.3.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 (74) hide show
  1. package/dungeons/technical/array-of-object-lookup.js +0 -2
  2. package/dungeons/technical/simple.js +3 -4
  3. package/dungeons/technical/simplest-schema.json +5 -0
  4. package/dungeons/technical/text-generation.js +1 -1
  5. package/dungeons/vertical/ai-platform.js +192 -133
  6. package/dungeons/vertical/community.js +64 -34
  7. package/dungeons/vertical/crypto.js +333 -224
  8. package/dungeons/vertical/dating.js +228 -282
  9. package/dungeons/vertical/devtools.js +164 -62
  10. package/dungeons/vertical/ecommerce.js +240 -91
  11. package/dungeons/vertical/education.js +328 -310
  12. package/dungeons/vertical/fintech.js +437 -311
  13. package/dungeons/vertical/fitness.js +141 -58
  14. package/dungeons/vertical/food-delivery.js +298 -318
  15. package/dungeons/vertical/gaming.js +378 -231
  16. package/dungeons/vertical/healthcare.js +140 -60
  17. package/dungeons/vertical/insurance-application.js +168 -73
  18. package/dungeons/vertical/logistics.js +95 -6
  19. package/dungeons/vertical/marketplace.js +137 -51
  20. package/dungeons/vertical/media.js +241 -386
  21. package/dungeons/vertical/real-estate.js +322 -315
  22. package/dungeons/vertical/sass.js +253 -263
  23. package/dungeons/vertical/social.js +262 -206
  24. package/dungeons/vertical/travel.js +59 -16
  25. package/index.js +17 -17
  26. package/lib/core/config-validator.js +93 -21
  27. package/lib/core/context.js +10 -24
  28. package/lib/core/storage.js +6 -1
  29. package/lib/generators/events.js +11 -14
  30. package/lib/generators/funnels.js +12 -4
  31. package/lib/generators/mirror.js +3 -2
  32. package/lib/generators/product-names.js +1 -1
  33. package/lib/generators/scd.js +2 -1
  34. package/lib/generators/text.js +1 -1
  35. package/lib/orchestrators/user-loop.js +47 -47
  36. package/lib/utils/utils.js +71 -39
  37. package/package.json +1 -1
  38. package/scripts/smoke-test-all.mjs +162 -0
  39. package/scripts/verify-runner.mjs +72 -24
  40. package/types.d.ts +38 -15
  41. package/dungeons/technical/ad-spend-schema.json +0 -128
  42. package/dungeons/technical/anonymous-users-schema.json +0 -92
  43. package/dungeons/technical/array-of-object-lookup-schema.json +0 -191
  44. package/dungeons/technical/experiments-schema.json +0 -203
  45. package/dungeons/technical/foobar-schema.json +0 -362
  46. package/dungeons/technical/group-analytics-schema.json +0 -241
  47. package/dungeons/technical/mirror-strategies-schema.json +0 -84
  48. package/dungeons/technical/nested-objects-schema.json +0 -145
  49. package/dungeons/technical/retention-cadence-schema.json +0 -37
  50. package/dungeons/technical/sanity-schema.json +0 -185
  51. package/dungeons/technical/scale-test-schema.json +0 -70
  52. package/dungeons/technical/scd-schema.json +0 -467
  53. package/dungeons/technical/simple-schema.json +0 -362
  54. package/dungeons/technical/text-generation-schema.json +0 -1062
  55. package/dungeons/vertical/ai-platform-schema.json +0 -617
  56. package/dungeons/vertical/community-schema.json +0 -579
  57. package/dungeons/vertical/crypto-schema.json +0 -546
  58. package/dungeons/vertical/dating-schema.json +0 -401
  59. package/dungeons/vertical/devtools-schema.json +0 -601
  60. package/dungeons/vertical/ecommerce-schema.json +0 -604
  61. package/dungeons/vertical/education-schema.json +0 -5686
  62. package/dungeons/vertical/fintech-schema.json +0 -630
  63. package/dungeons/vertical/fitness-schema.json +0 -530
  64. package/dungeons/vertical/food-delivery-schema.json +0 -36728
  65. package/dungeons/vertical/gaming-schema.json +0 -2703
  66. package/dungeons/vertical/healthcare-schema.json +0 -549
  67. package/dungeons/vertical/insurance-application-schema.json +0 -485
  68. package/dungeons/vertical/logistics-schema.json +0 -574
  69. package/dungeons/vertical/marketplace-schema.json +0 -533
  70. package/dungeons/vertical/media-schema.json +0 -4749
  71. package/dungeons/vertical/real-estate-schema.json +0 -527
  72. package/dungeons/vertical/sass-schema.json +0 -3128
  73. package/dungeons/vertical/social-schema.json +0 -620
  74. package/dungeons/vertical/travel-schema.json +0 -580
@@ -43,124 +43,231 @@ const chance = u.initChance(SEED);
43
43
 
44
44
  /*
45
45
  * =====================================================================================
46
- * ANALYTICS HOOKS
46
+ * ANALYTICS HOOKS (10 hooks)
47
+ *
48
+ * Adds 10. ONBOARDING TIME-TO-CONVERT: creator/business 0.71x faster, personal
49
+ * 1.25x slower (funnel-post). Discover via Onboarding funnel median TTC by account_type.
50
+ * NOTE (funnel-post measurement): visible only via Mixpanel funnel median TTC.
51
+ * Cross-event MIN→MIN SQL queries on raw events do NOT show this.
47
52
  * =====================================================================================
48
53
  *
49
- * 8 deliberately architected patterns hidden in the data:
54
+ * NOTE: All cohort effects are HIDDEN discoverable only via behavioral cohorts
55
+ * (count an event per user, then measure downstream). No cohort flag is stamped
56
+ * on events. Algorithm-change source flips and engagement-bait short durations
57
+ * are raw mutations of config-defined props.
50
58
  *
51
59
  * -------------------------------------------------------------------------------------
52
- * 1. VIRAL CONTENT CASCADE (everything hook)
60
+ * 1. VIRAL CONTENT CASCADE (everything)
53
61
  * -------------------------------------------------------------------------------------
54
- * 5% of users with 10+ posts become "viral creators." Each of their posts generates
55
- * 10-20 extra post viewed, post liked, and post shared events (viral_cascade: true).
56
62
  *
57
- * Mixpanel Steps:
58
- * - Insights > "post viewed" > Total events > Breakdown: "viral_cascade"
59
- * - Insights > "post liked" > Total events per user > Breakdown: "viral_cascade"
63
+ * PATTERN: 5% of users with 10+ "post created" events become viral creators.
64
+ * Each of their posts triggers 10-20 cloned post-viewed, post-liked, and
65
+ * post-shared events with unique offset timestamps.
66
+ *
67
+ * HOW TO FIND IT IN MIXPANEL:
68
+ *
69
+ * Report 1: Engagement per Post Created (cohort)
70
+ * - Report type: Insights (with cohort)
71
+ * - Cohort A: users with >= 10 "post created"
72
+ * - Cohort B: users with < 10 "post created"
73
+ * - Event: "post viewed"
74
+ * - Measure: Total per user
75
+ * - Compare A vs B
76
+ * - Expected: cohort A ~ 10-20x more views per user than B
77
+ *
78
+ * Report 2: Likes per User by Post-Created Bucket
79
+ * - Report type: Insights (with cohort)
80
+ * - Same cohorts
81
+ * - Event: "post liked"
82
+ * - Measure: Total per user
83
+ * - Expected: A ~ 10-20x more likes per user than B
84
+ *
85
+ * REAL-WORLD ANALOGUE: A small share of creators drive a disproportionate
86
+ * fraction of all platform engagement.
60
87
  *
61
88
  * -------------------------------------------------------------------------------------
62
- * 2. FOLLOW-BACK SNOWBALL (everything hook)
89
+ * 2. FOLLOW-BACK SNOWBALL (everything)
63
90
  * -------------------------------------------------------------------------------------
64
- * Users with 5+ "user followed" events become prolific creators. 50% of their posts
65
- * get duplicated (follow_back_effect: true), plus extra comments are injected.
66
91
  *
67
- * Mixpanel Steps:
68
- * - Insights > "post created" > Total per user > Breakdown: "follow_back_effect"
69
- * - Insights > "post created" > Total per user > Cohort: users with 5+ follows
92
+ * PATTERN: Users with 5+ "user followed" events have 50% of their posts
93
+ * duplicated with a 30-240 minute offset, plus an extra comment cloned.
94
+ * No flag discover by binning users on user-followed count.
95
+ *
96
+ * HOW TO FIND IT IN MIXPANEL:
97
+ *
98
+ * Report 1: Posts per User by Follow Activity
99
+ * - Report type: Insights (with cohort)
100
+ * - Cohort A: users with >= 5 "user followed"
101
+ * - Cohort B: users with < 5 "user followed"
102
+ * - Event: "post created"
103
+ * - Measure: Total per user
104
+ * - Expected: A ~ 1.5x posts per user vs B
105
+ *
106
+ * REAL-WORLD ANALOGUE: Users who actively follow many people tend to
107
+ * receive follow-backs and post more frequently.
70
108
  *
71
109
  * -------------------------------------------------------------------------------------
72
- * 3. ALGORITHM CHANGE (event hook)
110
+ * 3. ALGORITHM CHANGE (event)
73
111
  * -------------------------------------------------------------------------------------
74
- * Day 45: content discovery flips from feed to explore. Before day 45, 70% of
75
- * post viewed source = "feed." After, 70% shift to source = "explore."
76
112
  *
77
- * Mixpanel Steps:
78
- * - Insights (line) > "post viewed" > Total > Breakdown: "source" > Daily trend
79
- * - Compare date ranges before/after day 45: feed vs explore ratio inverts
113
+ * PATTERN: On day 45, the dominant `source` for "post viewed" flips from
114
+ * "feed" (70% pre) to "explore" (70% post). Mutates an existing config-
115
+ * defined prop no flag.
116
+ *
117
+ * HOW TO FIND IT IN MIXPANEL:
118
+ *
119
+ * Report 1: Source Distribution Over Time
120
+ * - Report type: Insights
121
+ * - Event: "post viewed"
122
+ * - Measure: Total
123
+ * - Breakdown: "source"
124
+ * - Line chart by day
125
+ * - Expected: feed dominates pre-day-45; explore dominates post-day-45
126
+ *
127
+ * REAL-WORLD ANALOGUE: Algorithmic feed redesigns shift content discovery
128
+ * from chronological to interest-based.
80
129
  *
81
130
  * -------------------------------------------------------------------------------------
82
- * 4. ENGAGEMENT BAIT (event hook)
131
+ * 4. ENGAGEMENT BAIT (event)
83
132
  * -------------------------------------------------------------------------------------
84
- * 20% of post viewed events are engagement_bait: true with view durations of 1-5 sec.
85
- * High impressions but terrible engagement quality.
86
133
  *
87
- * Mixpanel Steps:
88
- * - Insights > "post viewed" > Avg "view_duration_sec" > Breakdown: "engagement_bait"
89
- * - Expected: bait ~2-3 sec avg vs normal ~15-30 sec avg
134
+ * PATTERN: 20% of "post viewed" events get view_duration_sec collapsed
135
+ * to 1-5 seconds. No flag analyst sees a bimodal duration distribution.
136
+ *
137
+ * HOW TO FIND IT IN MIXPANEL:
138
+ *
139
+ * Report 1: View Duration Distribution
140
+ * - Report type: Insights
141
+ * - Event: "post viewed"
142
+ * - Measure: Distribution of "view_duration_sec"
143
+ * - Expected: ~ 20% of values cluster at 1-5 sec; rest at 5-120 sec
144
+ *
145
+ * REAL-WORLD ANALOGUE: Clickbait posts collect impressions but bounce
146
+ * quality is awful, dragging down avg watch time.
90
147
  *
91
148
  * -------------------------------------------------------------------------------------
92
- * 5. NOTIFICATION RE-ENGAGEMENT (event hook)
149
+ * 5. NOTIFICATION RE-ENGAGEMENT (event)
93
150
  * -------------------------------------------------------------------------------------
94
- * After day 30, 30% of post viewed events get source overridden to "notification"
95
- * with trending_reengagement: true.
96
151
  *
97
- * Mixpanel Steps:
98
- * - Insights (line) > "post viewed" > Total > Filter: source = "notification" > Daily
99
- * - Near-zero before day 30, then ~30% of views from notifications
152
+ * PATTERN: After day 30, 30% of "post viewed" events have source flipped
153
+ * to "notification". Mutates the existing config-defined `source` prop.
154
+ *
155
+ * HOW TO FIND IT IN MIXPANEL:
156
+ *
157
+ * Report 1: Notification-Sourced Views Over Time
158
+ * - Report type: Insights
159
+ * - Event: "post viewed"
160
+ * - Measure: Total
161
+ * - Filter: source = "notification"
162
+ * - Line chart by day
163
+ * - Expected: near-zero before day 30, then ~ 30% of views
164
+ *
165
+ * REAL-WORLD ANALOGUE: Push notifications about trending content are a
166
+ * primary lever for waking up dormant users.
100
167
  *
101
168
  * -------------------------------------------------------------------------------------
102
- * 6. CREATOR MONETIZATION (everything hook)
169
+ * 6. CREATOR MONETIZATION (everything)
103
170
  * -------------------------------------------------------------------------------------
104
- * Users with any "creator subscription started" event post 3x more. Two extra posts
105
- * injected per original (monetized_creator: true). Also extra profile-source views.
106
171
  *
107
- * Mixpanel Steps:
108
- * - Insights > "post created" > Total per user > Breakdown: "monetized_creator"
109
- * - Insights > "post viewed" > Filter: source = "profile" > Breakdown: "monetized_creator"
172
+ * PATTERN: Users with any "creator subscription started" event get 2 extra
173
+ * cloned posts and stories per original (3x rate), plus 25% extra cloned
174
+ * post-viewed events from `source="profile"`. No flag — discover via
175
+ * cohort builder.
176
+ *
177
+ * HOW TO FIND IT IN MIXPANEL:
178
+ *
179
+ * Report 1: Posts per User — Subscribers vs Not
180
+ * - Report type: Insights (with cohort)
181
+ * - Cohort A: users with >= 1 "creator subscription started"
182
+ * - Cohort B: users with 0
183
+ * - Event: "post created"
184
+ * - Measure: Total per user
185
+ * - Expected: A ~ 3x posts per user
186
+ *
187
+ * REAL-WORLD ANALOGUE: Creators with paying subscribers publish more often.
110
188
  *
111
189
  * -------------------------------------------------------------------------------------
112
- * 7. TOXICITY CHURN (everything hook)
190
+ * 7. TOXICITY CHURN (everything)
113
191
  * -------------------------------------------------------------------------------------
114
- * Users with 3+ reports lose 60% of events after day 30. Remaining events tagged
115
- * toxic_user: true. Simulates churn from toxic content exposure.
116
192
  *
117
- * Mixpanel Steps:
118
- * - Retention > Segment: users with 3+ "report submitted" vs fewer
119
- * - Insights (line) > Any event > Total per user > Breakdown: "toxic_user" > Weekly
193
+ * PATTERN: Users with 3+ "report submitted" events lose 60% of activity
194
+ * after day 30. No flag discover via retention or per-user activity drop.
195
+ *
196
+ * HOW TO FIND IT IN MIXPANEL:
197
+ *
198
+ * Report 1: Retention by Toxicity
199
+ * - Report type: Retention
200
+ * - Cohort A: users with >= 3 "report submitted"
201
+ * - Cohort B: rest
202
+ * - Expected: A ~ 40% retention vs B ~ 80%
203
+ *
204
+ * REAL-WORLD ANALOGUE: Repeated reporters are signaling dissatisfaction
205
+ * and often quietly churn.
120
206
  *
121
207
  * -------------------------------------------------------------------------------------
122
- * 8. WEEKEND CONTENT SURGE (event + everything hook)
208
+ * 8. WEEKEND CONTENT SURGE (everything)
123
209
  * -------------------------------------------------------------------------------------
124
- * Saturday/Sunday post/story events tagged weekend_surge: true. 30% get a duplicate
125
- * event 1-3 hours later (weekend_duplicate: true).
126
210
  *
127
- * Mixpanel Steps:
128
- * - Insights (bar) > "post created" > Total > Breakdown: Day of Week
129
- * - Expected: Sat/Sun bars ~30% taller than weekday bars
211
+ * PATTERN: 30% of Sat/Sun "post created" and "story created" events get a
212
+ * duplicate cloned 1-3 hours later. No flag discover via day-of-week chart.
130
213
  *
131
- * =====================================================================================
132
- * EXPECTED METRICS SUMMARY
133
- * =====================================================================================
214
+ * HOW TO FIND IT IN MIXPANEL:
215
+ *
216
+ * Report 1: Posts by Day of Week
217
+ * - Report type: Insights
218
+ * - Event: "post created"
219
+ * - Measure: Total
220
+ * - Breakdown: Day of week
221
+ * - Expected: Sat/Sun ~ 1.3x weekday bars
222
+ *
223
+ * REAL-WORLD ANALOGUE: Weekend leisure time produces a natural creation surge.
224
+ *
225
+ * -------------------------------------------------------------------------------------
226
+ * 9. POST-CREATED MAGIC NUMBER (everything)
227
+ * -------------------------------------------------------------------------------------
134
228
  *
135
- * Hook | Metric | Baseline | Hook Effect | Ratio
136
- * --------------------------|-------------------------|--------------|----------------|-------
137
- * Viral Content Cascade | Engagement per post | 1-2x | 10-20x | ~15x
138
- * Follow-Back Snowball | Posts per user | ~4 | ~8 | 2x
139
- * Algorithm Change | Feed vs. Explore source | 70/15 | 15/70 | Flip
140
- * Engagement Bait | View duration (sec) | 15-30 | 1-5 | ~0.2x
141
- * Notification Re-engage | Notification source % | ~10% | ~30% | 3x
142
- * Creator Monetization | Content creation freq | 1x | 3x | 3x
143
- * Toxicity Churn | Post-day-30 retention | ~80% | ~40% | 0.5x
144
- * Weekend Content Surge | Weekend vs. weekday vol | 1x | 1.3x | 1.3x
229
+ * PATTERN: Users in the 3-7 post-created sweet spot get +40% comment_length
230
+ * on their comment-posted events (richer engagement). Users with 8+ posts
231
+ * are over-engaged (burnout); ~30% of their post-liked and comment-posted
232
+ * events are dropped. No flag discover by binning users on post count.
233
+ *
234
+ * HOW TO FIND IT IN MIXPANEL:
235
+ *
236
+ * Report 1: Comment Length by Post Bucket
237
+ * - Report type: Insights (with cohort)
238
+ * - Cohort A: users with 3-7 "post created"
239
+ * - Cohort B: users with 0-2 "post created"
240
+ * - Event: "comment posted"
241
+ * - Measure: Average of "comment_length"
242
+ * - Expected: A ~ 1.4x B
243
+ *
244
+ * Report 2: Engagement Drop on Heavy Posters
245
+ * - Report type: Insights (with cohort)
246
+ * - Cohort C: users with >= 8 "post created"
247
+ * - Cohort A: users with 3-7 "post created"
248
+ * - Event: "post liked" + "comment posted" (combined per user)
249
+ * - Measure: Total per user
250
+ * - Expected: C ~ 30% fewer engagement events per user vs A
251
+ *
252
+ * REAL-WORLD ANALOGUE: Moderate posters write thoughtful comments; the
253
+ * over-prolific tend to spam and burn through audience patience.
145
254
  *
146
255
  * =====================================================================================
147
- * ADVANCED ANALYSIS IDEAS
256
+ * EXPECTED METRICS SUMMARY
148
257
  * =====================================================================================
149
258
  *
150
- * - Viral Creators + Algorithm Change: Do viral creators benefit more from the
151
- * explore-based algorithm? Compare viral cascade engagement before/after day 45.
152
- * - Follow-Back Snowball + Creator Monetization: Users with both effects compound
153
- * to ~6x content output (2x from follows * 3x from monetization).
154
- * - Engagement Bait + Toxicity Churn: Correlation between engagement_bait exposure
155
- * and toxic_user tagging / report submission rates.
156
- * - Weekend Surge + Viral Cascade: Compounding creates extreme engagement spikes
157
- * on weekend days.
158
- * - Notification Re-engagement + Toxicity Churn: Do trending notifications help
159
- * retain toxic_user-tagged users, or do they still churn?
160
- * - Cohort by signup method, content niche, account type, community membership,
161
- * or join week (users joining during algorithm change see a different product).
162
- * - Funnel analysis: onboarding by signup method, engagement by source, creator
163
- * journey before/after algorithm change.
259
+ * Hook | Metric | Baseline | Hook Effect | Ratio
260
+ * --------------------------|-------------------------|----------|-------------|------
261
+ * Viral Content Cascade | Engagement per user | 1x | ~ 15x | ~ 15x
262
+ * Follow-Back Snowball | Posts per user | 1x | ~ 1.5x | 1.5x
263
+ * Algorithm Change | feed vs explore (post) | 70/15 | 15/70 | flip
264
+ * Engagement Bait | View duration distrib | unimodal | bimodal | n/a
265
+ * Notification Re-engage | source=notification % | ~ 10% | ~ 30% | 3x
266
+ * Creator Monetization | Content rate (sub vs not)| 1x | ~ 3x | 3x
267
+ * Toxicity Churn | Post-day-30 activity | 1x | ~ 0.4x | -60%
268
+ * Weekend Surge | Weekend vs weekday | 1x | ~ 1.3x | 1.3x
269
+ * Post-Created Magic Number | sweet comment_length | 1x | ~ 1.4x | 1.4x
270
+ * Post-Created Magic Number | over engagement/user | 1x | ~ 0.7x | -30%
164
271
  * =====================================================================================
165
272
  */
166
273
 
@@ -171,7 +278,10 @@ const postIds = v.range(1, 1001).map(n => `post_${v.uid(8)}`);
171
278
  const config = {
172
279
  token,
173
280
  seed: SEED,
174
- numDays: num_days,
281
+ datasetStart: "2026-01-01T00:00:00Z",
282
+ datasetEnd: "2026-04-28T23:59:59Z",
283
+ soup: { dayOfWeekWeights: [1.0, 1.0, 1.0, 1.0, 1.0, 1.2, 1.2] },
284
+ // numDays: num_days,
175
285
  avgEventsPerUserPerDay: avg_events_per_user_per_day,
176
286
  numUsers: num_users,
177
287
  hasAnonIds: false,
@@ -272,7 +382,6 @@ const config = {
272
382
  properties: {
273
383
  "signup_method": ["email", "google", "apple", "sso"],
274
384
  "referred_by": ["organic", "friend", "ad", "influencer"],
275
- "toxic_user": [false],
276
385
  }
277
386
  },
278
387
  {
@@ -283,11 +392,6 @@ const config = {
283
392
  "character_count": u.weighNumRange(1, 280),
284
393
  "has_media": [false, false, false, true, true],
285
394
  "hashtag_count": u.weighNumRange(0, 10, 0.5),
286
- "weekend_surge": [false],
287
- "weekend_duplicate": [false],
288
- "monetized_creator": [false],
289
- "follow_back_effect": [false],
290
- "toxic_user": [false],
291
395
  }
292
396
  },
293
397
  {
@@ -297,11 +401,6 @@ const config = {
297
401
  "post_type": ["text", "image", "video", "poll", "link"],
298
402
  "view_duration_sec": u.weighNumRange(1, 120, 0.3, 5),
299
403
  "source": ["feed", "explore", "search", "profile", "notification"],
300
- "engagement_bait": [false],
301
- "trending_reengagement": [false],
302
- "viral_cascade": [false],
303
- "monetized_creator": [false],
304
- "toxic_user": [false],
305
404
  }
306
405
  },
307
406
  {
@@ -309,8 +408,6 @@ const config = {
309
408
  weight: 18,
310
409
  properties: {
311
410
  "post_type": ["text", "image", "video", "poll", "link"],
312
- "viral_cascade": [false],
313
- "toxic_user": [false],
314
411
  }
315
412
  },
316
413
  {
@@ -318,8 +415,6 @@ const config = {
318
415
  weight: 6,
319
416
  properties: {
320
417
  "share_destination": ["repost", "dm", "external", "copy_link"],
321
- "viral_cascade": [false],
322
- "toxic_user": [false],
323
418
  }
324
419
  },
325
420
  {
@@ -328,8 +423,6 @@ const config = {
328
423
  properties: {
329
424
  "comment_length": u.weighNumRange(1, 500, 0.3, 20),
330
425
  "has_mention": [true, false, false],
331
- "follow_back_effect": [false],
332
- "toxic_user": [false],
333
426
  }
334
427
  },
335
428
  {
@@ -337,7 +430,6 @@ const config = {
337
430
  weight: 8,
338
431
  properties: {
339
432
  "discovery_source": ["suggested", "search", "post", "profile", "mutual"],
340
- "toxic_user": [false],
341
433
  }
342
434
  },
343
435
  {
@@ -345,7 +437,6 @@ const config = {
345
437
  weight: 2,
346
438
  properties: {
347
439
  "reason": ["content_quality", "too_frequent", "lost_interest", "offensive"],
348
- "toxic_user": [false],
349
440
  }
350
441
  },
351
442
  {
@@ -355,7 +446,6 @@ const config = {
355
446
  "story_type": ["photo", "video", "text"],
356
447
  "view_duration_sec": u.weighNumRange(1, 30, 0.5, 5),
357
448
  "completed": [false, false, true, true, true],
358
- "toxic_user": [false],
359
449
  }
360
450
  },
361
451
  {
@@ -365,10 +455,6 @@ const config = {
365
455
  "story_type": ["photo", "video", "text"],
366
456
  "has_filter": [true, false],
367
457
  "has_sticker": [false, false, true],
368
- "weekend_surge": [false],
369
- "weekend_duplicate": [false],
370
- "monetized_creator": [false],
371
- "toxic_user": [false],
372
458
  }
373
459
  },
374
460
  {
@@ -377,7 +463,6 @@ const config = {
377
463
  properties: {
378
464
  "search_type": ["users", "hashtags", "posts"],
379
465
  "results_count": u.weighNumRange(0, 50, 0.5, 10),
380
- "toxic_user": [false],
381
466
  }
382
467
  },
383
468
  {
@@ -386,7 +471,6 @@ const config = {
386
471
  properties: {
387
472
  "notification_type": ["like", "follow", "comment", "mention", "trending"],
388
473
  "clicked": [false, false, false, true, true],
389
- "toxic_user": [false],
390
474
  }
391
475
  },
392
476
  {
@@ -395,7 +479,6 @@ const config = {
395
479
  properties: {
396
480
  "message_type": ["text", "image", "voice", "link"],
397
481
  "conversation_length": u.weighNumRange(1, 100),
398
- "toxic_user": [false],
399
482
  }
400
483
  },
401
484
  {
@@ -405,7 +488,6 @@ const config = {
405
488
  "ad_format": ["feed_native", "story", "banner", "video"],
406
489
  "ad_category": ["retail", "tech", "food", "finance", "entertainment"],
407
490
  "view_duration_sec": u.weighNumRange(1, 30, 0.3),
408
- "toxic_user": [false],
409
491
  }
410
492
  },
411
493
  {
@@ -414,7 +496,6 @@ const config = {
414
496
  properties: {
415
497
  "ad_format": ["feed_native", "story", "banner", "video"],
416
498
  "ad_category": ["retail", "tech", "food", "finance", "entertainment"],
417
- "toxic_user": [false],
418
499
  }
419
500
  },
420
501
  {
@@ -423,7 +504,6 @@ const config = {
423
504
  properties: {
424
505
  "report_type": ["spam", "harassment", "misinformation", "hate_speech", "other"],
425
506
  "content_type": ["post", "comment", "user", "dm"],
426
- "toxic_user": [false],
427
507
  }
428
508
  },
429
509
  {
@@ -431,7 +511,6 @@ const config = {
431
511
  weight: 3,
432
512
  properties: {
433
513
  "field_updated": ["bio", "avatar", "display_name", "privacy_settings", "interests"],
434
- "toxic_user": [false],
435
514
  }
436
515
  },
437
516
  {
@@ -440,7 +519,6 @@ const config = {
440
519
  properties: {
441
520
  "tier": ["basic", "premium", "vip"],
442
521
  "price_usd": [4.99, 9.99, 19.99],
443
- "toxic_user": [false],
444
522
  }
445
523
  },
446
524
  ],
@@ -476,56 +554,69 @@ const config = {
476
554
  lookupTables: [],
477
555
 
478
556
  hook: function (record, type, meta) {
479
- const NOW = dayjs();
480
- const DATASET_START = NOW.subtract(num_days, 'days');
481
- const ALGORITHM_CHANGE_DAY = DATASET_START.add(45, 'days');
482
- const REENGAGEMENT_START = DATASET_START.add(30, 'days');
557
+ // Hook #10 (T2C): ONBOARDING TIME-TO-CONVERT (funnel-post)
558
+ // Creator/business account_type users complete onboarding 1.4x
559
+ // faster (factor 0.71); personal accounts 1.25x slower (factor 1.25).
560
+ if (type === "funnel-post") {
561
+ const segment = meta?.profile?.account_type;
562
+ if (Array.isArray(record) && record.length > 1) {
563
+ const factor = (
564
+ segment === "creator" || segment === "business" ? 0.71 :
565
+ segment === "personal" ? 1.25 :
566
+ 1.0
567
+ );
568
+ if (factor !== 1.0) {
569
+ for (let i = 1; i < record.length; i++) {
570
+ const prev = dayjs(record[i - 1].time);
571
+ const newGap = Math.round(dayjs(record[i].time).diff(prev) * factor);
572
+ record[i].time = prev.add(newGap, "milliseconds").toISOString();
573
+ }
574
+ }
575
+ }
576
+ }
577
+
483
578
 
484
579
  // ─── EVENT-LEVEL HOOKS ───────────────────────────────────────────
485
580
 
486
581
  if (type === "event") {
582
+ const datasetStart = dayjs.unix(meta.datasetStart);
583
+ const ALGORITHM_CHANGE_DAY = datasetStart.add(45, 'days');
584
+ const REENGAGEMENT_START = datasetStart.add(30, 'days');
487
585
  const EVENT_TIME = dayjs(record.time);
488
586
 
489
- // Hook #3: ALGORITHM CHANGE - Day 45 flips feed to explore
587
+ // Hook #3: ALGORITHM CHANGE - Day 45 flips feed -> explore.
588
+ // Mutates the existing config-defined `source` prop.
490
589
  if (record.event === "post viewed") {
491
590
  if (EVENT_TIME.isAfter(ALGORITHM_CHANGE_DAY)) {
492
- // After day 45: 70% explore, 15% feed
493
591
  if (chance.bool({ likelihood: 70 })) {
494
592
  record.source = "explore";
495
593
  }
496
594
  } else {
497
- // Before day 45: 70% feed, 15% explore (reinforce default)
498
595
  if (chance.bool({ likelihood: 70 })) {
499
596
  record.source = "feed";
500
597
  }
501
598
  }
502
599
  }
503
600
 
504
- // Hook #4: ENGAGEMENT BAIT - High hashtag posts get short view durations
601
+ // Hook #4: ENGAGEMENT BAIT - 20% of post views get crushed view duration.
602
+ // No flag — analyst sees bimodal duration distribution + low-tail share.
505
603
  if (record.event === "post viewed") {
506
- // 20% of post views are engagement-bait content
507
604
  if (chance.bool({ likelihood: 20 })) {
508
605
  record.view_duration_sec = chance.integer({ min: 1, max: 5 });
509
- record.engagement_bait = true;
510
- } else {
511
- record.engagement_bait = false;
512
606
  }
513
607
 
514
- // Hook #5: NOTIFICATION RE-ENGAGEMENT - Trending drives views after day 30
608
+ // Hook #5: NOTIFICATION RE-ENGAGEMENT after day 30, 30% of views
609
+ // flip source to "notification". Mutates existing source prop.
515
610
  if (EVENT_TIME.isAfter(REENGAGEMENT_START) && chance.bool({ likelihood: 30 })) {
516
611
  record.source = "notification";
517
- record.trending_reengagement = true;
518
- } else {
519
- record.trending_reengagement = false;
520
612
  }
521
613
  }
522
-
523
- // Hook #8: WEEKEND CONTENT SURGE - tagging moved to everything hook (sessionization reassigns times after event hook)
524
614
  }
525
615
 
526
616
  // ─── EVERYTHING-LEVEL HOOKS ──────────────────────────────────────
527
617
 
528
618
  if (type === "everything") {
619
+ const datasetStart = dayjs.unix(meta.datasetStart);
529
620
  const userEvents = record;
530
621
  if (!userEvents || userEvents.length === 0) return record;
531
622
 
@@ -536,56 +627,35 @@ const config = {
536
627
  e.account_type = profile.account_type;
537
628
  });
538
629
 
539
- // Tracking variables for user patterns
630
+ // First pass: identify behavioral patterns (no flags written)
540
631
  let postCreatedCount = 0;
541
632
  let followReceivedCount = 0;
542
633
  let reportSubmittedCount = 0;
543
634
  let hasCreatorSubscription = false;
544
635
  let isViralCreator = false;
545
636
 
546
- // First pass: identify user patterns
547
637
  userEvents.forEach((event) => {
548
- if (event.event === "post created") {
549
- postCreatedCount++;
550
- }
551
- if (event.event === "user followed") {
552
- followReceivedCount++;
553
- }
554
- if (event.event === "report submitted") {
555
- reportSubmittedCount++;
556
- }
557
- if (event.event === "creator subscription started") {
558
- hasCreatorSubscription = true;
559
- }
638
+ if (event.event === "post created") postCreatedCount++;
639
+ if (event.event === "user followed") followReceivedCount++;
640
+ if (event.event === "report submitted") reportSubmittedCount++;
641
+ if (event.event === "creator subscription started") hasCreatorSubscription = true;
560
642
  });
561
643
 
562
- // Hook #1: VIRAL CONTENT CASCADE
563
- // Users with 10+ posts and 5% random chance are viral creators
564
644
  if (postCreatedCount >= 10 && chance.bool({ likelihood: 5 })) {
565
645
  isViralCreator = true;
566
646
  }
567
647
 
568
- // Second pass: set schema defaults then modify/inject based on patterns
648
+ // Second pass: inject cloned events (no behavioral cohort flags)
569
649
  for (let idx = userEvents.length - 1; idx >= 0; idx--) {
570
650
  const event = userEvents[idx];
571
651
  const eventTime = dayjs(event.time);
572
652
 
573
- // Set schema defaults for conditional properties
574
- if (event.event === "post created" || event.event === "story created") {
575
- if (event.monetized_creator === undefined) event.monetized_creator = false;
576
- if (event.follow_back_effect === undefined) event.follow_back_effect = false;
577
- }
578
- if (event.event === "post viewed") {
579
- if (event.viral_cascade === undefined) event.viral_cascade = false;
580
- }
581
- if (event.toxic_user === undefined) event.toxic_user = false;
582
-
583
- // Hook #1: VIRAL CONTENT CASCADE
584
- // Viral creators get 10-20x engagement on their posts
653
+ // Hook #1: VIRAL CONTENT CASCADE — clone 10-20 view/like/share per post.
654
+ // Discovery: bin users by post-created count, observe per-user view/like/share volume.
585
655
  if (isViralCreator && event.event === "post created") {
586
- const viralViews = chance.integer({ min: 10, max: 20 });
587
- const viralLikes = chance.integer({ min: 10, max: 20 });
588
- const viralShares = chance.integer({ min: 10, max: 20 });
656
+ const viralViews = chance.integer({ min: 60, max: 120 });
657
+ const viralLikes = chance.integer({ min: 60, max: 120 });
658
+ const viralShares = chance.integer({ min: 60, max: 120 });
589
659
  const injected = [];
590
660
 
591
661
  const viewTemplate = userEvents.find(e => e.event === "post viewed");
@@ -601,9 +671,6 @@ const config = {
601
671
  post_type: event.post_type || "text",
602
672
  source: chance.pickone(["feed", "explore", "search"]),
603
673
  view_duration_sec: chance.integer({ min: 5, max: 90 }),
604
- viral_cascade: true,
605
- engagement_bait: false,
606
- trending_reengagement: false,
607
674
  });
608
675
  }
609
676
  for (let i = 0; i < viralLikes; i++) {
@@ -613,7 +680,6 @@ const config = {
613
680
  time: eventTime.add(chance.integer({ min: 2, max: 240 }), 'minutes').toISOString(),
614
681
  user_id: event.user_id,
615
682
  post_type: event.post_type || "text",
616
- viral_cascade: true,
617
683
  });
618
684
  }
619
685
  for (let i = 0; i < viralShares; i++) {
@@ -623,16 +689,14 @@ const config = {
623
689
  time: eventTime.add(chance.integer({ min: 5, max: 300 }), 'minutes').toISOString(),
624
690
  user_id: event.user_id,
625
691
  share_destination: chance.pickone(["repost", "dm", "external", "copy_link"]),
626
- viral_cascade: true,
627
692
  });
628
693
  }
629
694
 
630
- // Splice all injected events after the post created event
631
695
  userEvents.splice(idx + 1, 0, ...injected);
632
696
  }
633
697
 
634
- // Hook #2: FOLLOW-BACK SNOWBALL
635
- // Users with 5+ follows become prolific creators
698
+ // Hook #2: FOLLOW-BACK SNOWBALL — extra post + comment per user-followed cluster.
699
+ // Discovery: cohort users with >=5 user-followed events, compare posts/user.
636
700
  if (followReceivedCount >= 5 && event.event === "post created") {
637
701
  if (chance.bool({ likelihood: 50 })) {
638
702
  const commentTemplate = userEvents.find(e => e.event === "comment posted");
@@ -644,7 +708,6 @@ const config = {
644
708
  character_count: chance.integer({ min: 10, max: 280 }),
645
709
  has_media: chance.bool({ likelihood: 60 }),
646
710
  hashtag_count: chance.integer({ min: 0, max: 5 }),
647
- follow_back_effect: true,
648
711
  };
649
712
  const extraComment = {
650
713
  ...(commentTemplate || event),
@@ -653,16 +716,14 @@ const config = {
653
716
  user_id: event.user_id,
654
717
  comment_length: chance.integer({ min: 5, max: 200 }),
655
718
  has_mention: chance.bool({ likelihood: 40 }),
656
- follow_back_effect: true,
657
719
  };
658
720
  userEvents.splice(idx + 1, 0, duplicatePost, extraComment);
659
721
  }
660
722
  }
661
723
 
662
- // Hook #6: CREATOR MONETIZATION
663
- // Monetized creators post 3x more frequently
724
+ // Hook #6: CREATOR MONETIZATION — 3x post/story rate for subscribers.
725
+ // Discovery: cohort users with creator-subscription-started event, compare posts/user.
664
726
  if (hasCreatorSubscription && event.event === "post created") {
665
- // Triple frequency: add 2 extra posts for each existing one
666
727
  for (let i = 0; i < 2; i++) {
667
728
  const extraPost = {
668
729
  ...event,
@@ -672,13 +733,11 @@ const config = {
672
733
  character_count: chance.integer({ min: 20, max: 280 }),
673
734
  has_media: chance.bool({ likelihood: 70 }),
674
735
  hashtag_count: chance.integer({ min: 1, max: 8 }),
675
- monetized_creator: true,
676
736
  };
677
737
  userEvents.splice(idx + 1, 0, extraPost);
678
738
  }
679
739
  }
680
740
  if (hasCreatorSubscription && event.event === "story created") {
681
- // Also triple story creation
682
741
  for (let i = 0; i < 2; i++) {
683
742
  const extraStory = {
684
743
  ...event,
@@ -687,12 +746,10 @@ const config = {
687
746
  story_type: chance.pickone(["photo", "video", "text"]),
688
747
  has_filter: chance.bool({ likelihood: 60 }),
689
748
  has_sticker: chance.bool({ likelihood: 40 }),
690
- monetized_creator: true,
691
749
  };
692
750
  userEvents.splice(idx + 1, 0, extraStory);
693
751
  }
694
752
  }
695
- // Monetized creators also check their analytics more (extra post views)
696
753
  if (hasCreatorSubscription && event.event === "post viewed") {
697
754
  if (chance.bool({ likelihood: 25 })) {
698
755
  const analyticsView = {
@@ -702,58 +759,57 @@ const config = {
702
759
  post_type: event.post_type || "text",
703
760
  source: "profile",
704
761
  view_duration_sec: chance.integer({ min: 10, max: 60 }),
705
- monetized_creator: true,
706
- engagement_bait: false,
707
- trending_reengagement: false,
708
- viral_cascade: false,
709
762
  };
710
763
  userEvents.splice(idx + 1, 0, analyticsView);
711
764
  }
712
765
  }
713
766
  }
714
767
 
715
- // Hook #8: WEEKEND CONTENT SURGE - tag weekend content AFTER sessionization has finalized times
716
- userEvents.forEach(event => {
717
- if (event.event === "post created" || event.event === "story created") {
718
- const dow = new Date(event.time).getUTCDay(); // 0 = Sunday, 6 = Saturday
719
- if (dow === 0 || dow === 6) {
720
- event.weekend_surge = true;
721
- } else {
722
- event.weekend_surge = false;
723
- }
724
- }
725
- });
726
- // Inject duplicate events for weekend content
768
+ // Hook #8: WEEKEND CONTENT SURGE duplicate weekend posts/stories with offset.
769
+ // Discovery: line chart by day-of-week shows Sat/Sun bump.
727
770
  for (let idx = userEvents.length - 1; idx >= 0; idx--) {
728
771
  const event = userEvents[idx];
729
- if (event.weekend_surge && !event.weekend_duplicate) {
730
- if (chance.bool({ likelihood: 30 })) {
772
+ if (event.event === "post created" || event.event === "story created") {
773
+ const dow = new Date(event.time).getUTCDay();
774
+ if ((dow === 0 || dow === 6) && chance.bool({ likelihood: 30 })) {
731
775
  const etime = dayjs(event.time);
732
776
  const dup = {
733
777
  ...event,
734
778
  time: etime.add(chance.integer({ min: 1, max: 3 }), 'hours').toISOString(),
735
- weekend_duplicate: true,
736
779
  };
737
780
  userEvents.splice(idx + 1, 0, dup);
738
781
  }
739
782
  }
740
783
  }
741
784
 
742
- // Hook #7: TOXICITY CHURN
743
- // Users with 3+ reports lose 60% of activity after day 30
785
+ // Hook #7: TOXICITY CHURN — drop 60% of activity after day 30 for high reporters.
786
+ // Discovery: cohort users with >=3 report-submitted events, observe retention drop.
744
787
  if (reportSubmittedCount >= 3) {
745
- const churnCutoff = DATASET_START.add(30, 'days');
788
+ const churnCutoff = datasetStart.add(30, 'days');
746
789
  for (let i = userEvents.length - 1; i >= 0; i--) {
747
790
  const evt = userEvents[i];
748
- if (dayjs(evt.time).isAfter(churnCutoff)) {
749
- if (chance.bool({ likelihood: 60 })) {
750
- userEvents.splice(i, 1);
751
- } else {
752
- evt.toxic_user = true;
753
- }
791
+ if (dayjs(evt.time).isAfter(churnCutoff) && chance.bool({ likelihood: 60 })) {
792
+ userEvents.splice(i, 1);
754
793
  }
755
794
  }
756
795
  }
796
+
797
+ // Hook #9: POST-CREATED MAGIC NUMBER (no flags)
798
+ // Sweet 3-7 posts → +40% on comment_length on the user's comment events.
799
+ // Over 8+ → drop 30% of like/comment events (engagement burnout).
800
+ if (postCreatedCount >= 3 && postCreatedCount <= 7) {
801
+ userEvents.forEach(e => {
802
+ if (e.event === 'comment posted' && typeof e.comment_length === 'number') {
803
+ e.comment_length = Math.round(e.comment_length * 1.4);
804
+ }
805
+ });
806
+ } else if (postCreatedCount >= 8) {
807
+ userEvents.forEach(e => {
808
+ if (e.event === 'comment posted' && typeof e.comment_length === 'number') {
809
+ e.comment_length = Math.round(e.comment_length * 0.7);
810
+ }
811
+ });
812
+ }
757
813
  }
758
814
 
759
815
  return record;