@ak--47/dungeon-master 1.4.4 → 1.5.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.
- package/.claude/skills/analyze-soup/SKILL.md +158 -0
- package/.claude/skills/create-dungeon/SKILL.md +464 -0
- package/.claude/skills/verify-dungeon/SKILL.md +157 -0
- package/.claude/skills/verify-dungeon/references/counting-semantics.md +161 -0
- package/.claude/skills/verify-dungeon/references/report-format.md +216 -0
- package/.claude/skills/verify-dungeon/references/sql-recipes.md +857 -0
- package/.claude/skills/write-hooks/SKILL.md +468 -0
- package/CHANGELOG.md +147 -0
- package/HOOKS.md +1243 -597
- package/README.md +140 -5
- package/dungeons/technical/ad-spend.js +1 -1
- package/dungeons/technical/anonymous-users.js +1 -1
- package/dungeons/technical/array-of-object-lookup.js +1 -1
- package/dungeons/technical/datagen-v15-verify.js +74 -0
- package/dungeons/technical/experiments.js +1 -1
- package/dungeons/technical/foobar.js +1 -1
- package/dungeons/technical/group-analytics.js +1 -1
- package/dungeons/technical/mirror-strategies.js +1 -1
- package/dungeons/technical/nested-objects.js +1 -1
- package/dungeons/technical/retention-cadence.js +1 -1
- package/dungeons/technical/sanity.js +1 -1
- package/dungeons/technical/scale-test.js +1 -1
- package/dungeons/technical/scd.js +1 -1
- package/dungeons/technical/simple.js +1 -1
- package/dungeons/technical/simplest.js +74 -20
- package/dungeons/technical/text-generation.js +1 -1
- package/dungeons/vertical/ai-platform.js +4 -0
- package/dungeons/vertical/community.js +9 -3
- package/dungeons/vertical/crypto.js +5 -0
- package/dungeons/vertical/dating.js +23 -10
- package/dungeons/vertical/devtools.js +10 -0
- package/dungeons/vertical/ecommerce.js +6 -0
- package/dungeons/vertical/education.js +11 -0
- package/dungeons/vertical/fintech.js +13 -0
- package/dungeons/vertical/fitness.js +10 -0
- package/dungeons/vertical/food-delivery.js +9 -0
- package/dungeons/vertical/gaming.js +10 -0
- package/dungeons/vertical/healthcare.js +5 -0
- package/dungeons/vertical/insurance-application.js +10 -0
- package/dungeons/vertical/logistics.js +8 -1
- package/dungeons/vertical/marketplace.js +7 -0
- package/dungeons/vertical/media.js +8 -0
- package/dungeons/vertical/real-estate.js +7 -1
- package/dungeons/vertical/sass.js +12 -0
- package/dungeons/vertical/social.js +9 -0
- package/dungeons/vertical/travel.js +5 -0
- package/index.js +45 -7
- package/lib/core/config-validator.js +270 -7
- package/lib/core/context.js +58 -0
- package/lib/core/dungeon-loader.js +2 -5
- package/lib/generators/events.js +12 -13
- package/lib/generators/funnels.js +72 -1
- package/lib/hook-helpers/index.js +1 -0
- package/lib/hook-helpers/inject.js +95 -0
- package/lib/orchestrators/mixpanel-sender.js +27 -1
- package/lib/orchestrators/user-loop.js +488 -29
- package/lib/templates/macro-presets.js +39 -9
- package/lib/utils/utils.js +16 -79
- package/lib/verify/counting.js +320 -0
- package/lib/verify/emulate-breakdown.js +512 -108
- package/lib/verify/funnel-engine.js +539 -0
- package/lib/verify/identity.js +78 -0
- package/lib/verify/index.js +19 -0
- package/lib/verify/verify-dungeon.js +58 -0
- package/package.json +4 -2
- package/types.d.ts +314 -4
- package/scripts/smoke-test-all.mjs +0 -162
|
@@ -3,7 +3,7 @@ const SEED = "simple is best";
|
|
|
3
3
|
const num_days = 100;
|
|
4
4
|
const num_users = 2_500;
|
|
5
5
|
const avg_events_per_user_per_day = 1;
|
|
6
|
-
let token = "
|
|
6
|
+
let token = "";
|
|
7
7
|
|
|
8
8
|
// ── env overrides ──
|
|
9
9
|
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
@@ -14,7 +14,7 @@ import dayjs from "dayjs";
|
|
|
14
14
|
import utc from "dayjs/plugin/utc.js";
|
|
15
15
|
dayjs.extend(utc);
|
|
16
16
|
import { uid, comma } from 'ak-tools';
|
|
17
|
-
import { pickAWinner, weighNumRange, date, integer, weighChoices } from "../../lib/utils/utils.js";
|
|
17
|
+
import { pickAWinner, weighNumRange, date, integer, weighChoices, dateRange, listOf, objectList } from "../../lib/utils/utils.js";
|
|
18
18
|
|
|
19
19
|
/** @typedef {import("../../types").Dungeon} Config */
|
|
20
20
|
const itemCategories = ["Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden", "Pet", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art", "Food", "Appliances", "Office", "Wedding", "Software"];
|
|
@@ -26,20 +26,29 @@ const videoCategories = ["funny", "educational", "inspirational", "music", "news
|
|
|
26
26
|
* DATASET OVERVIEW
|
|
27
27
|
* ═══════════════════════════════════════════════════════════════
|
|
28
28
|
*
|
|
29
|
-
* Simplest E-Commerce App — the
|
|
29
|
+
* Simplest E-Commerce App — the canonical no-hook baseline dungeon.
|
|
30
30
|
* - 2,500 users over 100 days, ~250K events
|
|
31
31
|
* - 25 event types covering browse, search, cart, checkout,
|
|
32
32
|
* notifications, reviews, wishlists, rewards
|
|
33
|
-
* - 11 funnels (signup, purchase,
|
|
33
|
+
* - 11 funnels with diverse weights (1-10) covering signup, purchase,
|
|
34
|
+
* content engagement, browse-to-cart, post-purchase advocacy, etc.
|
|
35
|
+
* - At least one property of every Mixpanel data type:
|
|
36
|
+
* string, numeric, boolean, date, list, object, list-of-objects
|
|
37
|
+
* on both event properties and user properties.
|
|
34
38
|
* - No device/location/campaign data — pure event stream
|
|
39
|
+
* - **No hooks.** This is the engine-validation baseline; engine + TimeSoup
|
|
40
|
+
* alone produce the trend shape. Hook authors should reach for vertical
|
|
41
|
+
* dungeons or write hooks against this template; do NOT add hooks here.
|
|
35
42
|
*
|
|
36
43
|
* ═══════════════════════════════════════════════════════════════
|
|
37
|
-
*
|
|
44
|
+
* USE CASES
|
|
38
45
|
* ═══════════════════════════════════════════════════════════════
|
|
39
46
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
47
|
+
* - Engine sweep harness baseline (`scripts/sweep-engine.mjs`). Strict-bar
|
|
48
|
+
* regression testing across the 184-combo macro/born/rate/active-day matrix.
|
|
49
|
+
* - Reference for "what an average dungeon looks like" — diverse events
|
|
50
|
+
* + funnels + user props with no hook architecting on top.
|
|
51
|
+
* - Starting template for new dungeons before adding bespoke hooks.
|
|
43
52
|
*/
|
|
44
53
|
|
|
45
54
|
/** @type {import('../types.js').Dungeon} */
|
|
@@ -69,6 +78,7 @@ const config = {
|
|
|
69
78
|
{
|
|
70
79
|
event: "page view",
|
|
71
80
|
weight: 10,
|
|
81
|
+
isStrictEvent: false,
|
|
72
82
|
properties: {
|
|
73
83
|
page: ["/", "/", "/help", "/account", "/pricing", "/product", "/about", "/blog"],
|
|
74
84
|
utm_source: ["$organic", "$organic", "$organic", "$organic", "google", "google", "google", "facebook", "facebook", "twitter", "linkedin"],
|
|
@@ -77,6 +87,7 @@ const config = {
|
|
|
77
87
|
{
|
|
78
88
|
event: "sign up",
|
|
79
89
|
weight: 1,
|
|
90
|
+
isStrictEvent: false,
|
|
80
91
|
isFirstEvent: true,
|
|
81
92
|
properties: {
|
|
82
93
|
signupMethod: ["email", "google", "facebook", "github"],
|
|
@@ -86,6 +97,7 @@ const config = {
|
|
|
86
97
|
{
|
|
87
98
|
event: "login",
|
|
88
99
|
weight: 8,
|
|
100
|
+
isStrictEvent: false,
|
|
89
101
|
properties: {
|
|
90
102
|
method: ["password", "google", "facebook", "github"],
|
|
91
103
|
}
|
|
@@ -93,6 +105,7 @@ const config = {
|
|
|
93
105
|
{
|
|
94
106
|
event: "search",
|
|
95
107
|
weight: 7,
|
|
108
|
+
isStrictEvent: false,
|
|
96
109
|
properties: {
|
|
97
110
|
query_length: weighNumRange(1, 50),
|
|
98
111
|
resultsReturned: weighNumRange(0, 100, .25),
|
|
@@ -102,16 +115,22 @@ const config = {
|
|
|
102
115
|
{
|
|
103
116
|
event: "view item",
|
|
104
117
|
weight: 9,
|
|
118
|
+
isStrictEvent: false,
|
|
105
119
|
properties: {
|
|
106
120
|
isFeaturedItem: [true, false, false],
|
|
107
121
|
itemCategory: pickAWinner(itemCategories, integer(0, 27)),
|
|
108
122
|
price: weighNumRange(5, 500, .25),
|
|
109
123
|
rating: weighNumRange(1, 5),
|
|
124
|
+
// list (array of strings) — Mixpanel "List" property type
|
|
125
|
+
tags: listOf(["new", "sale", "limited", "popular", "trending", "exclusive", "clearance", "preorder"], { min: 1, max: 3 }),
|
|
126
|
+
// date — Mixpanel "Date" property type, default ISO string
|
|
127
|
+
lastRestockedAt: dateRange(),
|
|
110
128
|
}
|
|
111
129
|
},
|
|
112
130
|
{
|
|
113
131
|
event: "add to cart",
|
|
114
132
|
weight: 5,
|
|
133
|
+
isStrictEvent: false,
|
|
115
134
|
properties: {
|
|
116
135
|
amount: weighNumRange(5, 500, .25),
|
|
117
136
|
itemCategory: pickAWinner(itemCategories, integer(0, 27)),
|
|
@@ -121,16 +140,27 @@ const config = {
|
|
|
121
140
|
{
|
|
122
141
|
event: "checkout",
|
|
123
142
|
weight: 3,
|
|
143
|
+
isStrictEvent: false,
|
|
124
144
|
properties: {
|
|
125
145
|
amount: weighNumRange(10, 500, .25),
|
|
126
146
|
currency: ["USD", "CAD", "EUR", "JPY"],
|
|
127
147
|
coupon: weighChoices(["none", "none", "none", "none", "10%OFF", "20%OFF", "30%OFF"]),
|
|
128
148
|
numItems: weighNumRange(1, 10),
|
|
149
|
+
// object — Mixpanel "Object" property type (plain object, returned as-is)
|
|
150
|
+
billingAddress: { country: "US", region: "CA", postalCode: "94016" },
|
|
151
|
+
// list of objects — Mixpanel "List of Objects" property type
|
|
152
|
+
lineItems: objectList({
|
|
153
|
+
sku: weighNumRange(10000, 99999),
|
|
154
|
+
name: pickAWinner(itemCategories, integer(0, 27)),
|
|
155
|
+
qty: [1, 1, 1, 2, 2, 3],
|
|
156
|
+
price: weighNumRange(5, 200, .25),
|
|
157
|
+
}, { min: 1, max: 5 }),
|
|
129
158
|
}
|
|
130
159
|
},
|
|
131
160
|
{
|
|
132
161
|
event: "watch video",
|
|
133
162
|
weight: 6,
|
|
163
|
+
isStrictEvent: false,
|
|
134
164
|
properties: {
|
|
135
165
|
videoCategory: pickAWinner(videoCategories, integer(0, 9)),
|
|
136
166
|
watchTimeSec: weighNumRange(10, 600, .25),
|
|
@@ -140,6 +170,7 @@ const config = {
|
|
|
140
170
|
{
|
|
141
171
|
event: "share content",
|
|
142
172
|
weight: 2,
|
|
173
|
+
isStrictEvent: false,
|
|
143
174
|
properties: {
|
|
144
175
|
platform: ["twitter", "facebook", "linkedin", "email", "link"],
|
|
145
176
|
contentType: ["video", "product", "article"],
|
|
@@ -148,6 +179,7 @@ const config = {
|
|
|
148
179
|
{
|
|
149
180
|
event: "rate item",
|
|
150
181
|
weight: 4,
|
|
182
|
+
isStrictEvent: false,
|
|
151
183
|
properties: {
|
|
152
184
|
rating: weighNumRange(1, 5),
|
|
153
185
|
itemCategory: pickAWinner(itemCategories, integer(0, 27)),
|
|
@@ -160,6 +192,7 @@ const config = {
|
|
|
160
192
|
{
|
|
161
193
|
event: "support ticket",
|
|
162
194
|
weight: 2,
|
|
195
|
+
isStrictEvent: false,
|
|
163
196
|
properties: {
|
|
164
197
|
priority: weighChoices(["low", "low", "medium", "medium", "medium", "high"]),
|
|
165
198
|
category: ["billing", "technical", "account", "shipping", "returns"],
|
|
@@ -168,6 +201,7 @@ const config = {
|
|
|
168
201
|
{
|
|
169
202
|
event: "add to wishlist",
|
|
170
203
|
weight: 4,
|
|
204
|
+
isStrictEvent: false,
|
|
171
205
|
properties: {
|
|
172
206
|
itemCategory: pickAWinner(itemCategories, integer(0, 27)),
|
|
173
207
|
price: weighNumRange(5, 500, .25),
|
|
@@ -176,6 +210,7 @@ const config = {
|
|
|
176
210
|
{
|
|
177
211
|
event: "remove from cart",
|
|
178
212
|
weight: 3,
|
|
213
|
+
isStrictEvent: false,
|
|
179
214
|
properties: {
|
|
180
215
|
reason: weighChoices(["changed mind", "too expensive", "found better", "duplicate", "changed mind", "changed mind"]),
|
|
181
216
|
}
|
|
@@ -183,6 +218,7 @@ const config = {
|
|
|
183
218
|
{
|
|
184
219
|
event: "apply coupon",
|
|
185
220
|
weight: 2,
|
|
221
|
+
isStrictEvent: false,
|
|
186
222
|
properties: {
|
|
187
223
|
couponCode: weighChoices(["SAVE10", "SAVE20", "WELCOME", "FREESHIP", "VIP30", "SAVE10", "SAVE10"]),
|
|
188
224
|
discountPercent: weighNumRange(5, 50),
|
|
@@ -191,6 +227,7 @@ const config = {
|
|
|
191
227
|
{
|
|
192
228
|
event: "notification received",
|
|
193
229
|
weight: 7,
|
|
230
|
+
isStrictEvent: false,
|
|
194
231
|
properties: {
|
|
195
232
|
channel: ["push", "email", "in-app", "sms"],
|
|
196
233
|
type: ["promo", "order update", "recommendation", "reminder"],
|
|
@@ -199,6 +236,7 @@ const config = {
|
|
|
199
236
|
{
|
|
200
237
|
event: "notification clicked",
|
|
201
238
|
weight: 5,
|
|
239
|
+
isStrictEvent: false,
|
|
202
240
|
properties: {
|
|
203
241
|
channel: ["push", "email", "in-app", "sms"],
|
|
204
242
|
type: ["promo", "order update", "recommendation", "reminder"],
|
|
@@ -207,6 +245,7 @@ const config = {
|
|
|
207
245
|
{
|
|
208
246
|
event: "add payment method",
|
|
209
247
|
weight: 1,
|
|
248
|
+
isStrictEvent: false,
|
|
210
249
|
properties: {
|
|
211
250
|
type: ["credit card", "debit card", "paypal", "apple pay", "google pay"],
|
|
212
251
|
}
|
|
@@ -214,6 +253,7 @@ const config = {
|
|
|
214
253
|
{
|
|
215
254
|
event: "update profile",
|
|
216
255
|
weight: 3,
|
|
256
|
+
isStrictEvent: false,
|
|
217
257
|
properties: {
|
|
218
258
|
field: ["avatar", "name", "email", "address", "phone", "preferences"],
|
|
219
259
|
}
|
|
@@ -221,6 +261,7 @@ const config = {
|
|
|
221
261
|
{
|
|
222
262
|
event: "invite friend",
|
|
223
263
|
weight: 1,
|
|
264
|
+
isStrictEvent: false,
|
|
224
265
|
properties: {
|
|
225
266
|
method: ["email", "link", "sms"],
|
|
226
267
|
}
|
|
@@ -228,6 +269,7 @@ const config = {
|
|
|
228
269
|
{
|
|
229
270
|
event: "view category",
|
|
230
271
|
weight: 8,
|
|
272
|
+
isStrictEvent: false,
|
|
231
273
|
properties: {
|
|
232
274
|
category: pickAWinner(itemCategories, integer(0, 27)),
|
|
233
275
|
sortBy: ["popular", "newest", "price low", "price high", "rating"],
|
|
@@ -236,6 +278,7 @@ const config = {
|
|
|
236
278
|
{
|
|
237
279
|
event: "save address",
|
|
238
280
|
weight: 1,
|
|
281
|
+
isStrictEvent: false,
|
|
239
282
|
properties: {
|
|
240
283
|
type: ["home", "work", "other"],
|
|
241
284
|
}
|
|
@@ -243,6 +286,7 @@ const config = {
|
|
|
243
286
|
{
|
|
244
287
|
event: "compare items",
|
|
245
288
|
weight: 3,
|
|
289
|
+
isStrictEvent: false,
|
|
246
290
|
properties: {
|
|
247
291
|
numItems: weighNumRange(2, 5),
|
|
248
292
|
itemCategory: pickAWinner(itemCategories, integer(0, 27)),
|
|
@@ -251,6 +295,7 @@ const config = {
|
|
|
251
295
|
{
|
|
252
296
|
event: "subscribe newsletter",
|
|
253
297
|
weight: 1,
|
|
298
|
+
isStrictEvent: false,
|
|
254
299
|
properties: {
|
|
255
300
|
frequency: ["daily", "weekly", "monthly"],
|
|
256
301
|
topics: ["deals", "new arrivals", "recommendations", "deals", "deals"],
|
|
@@ -259,6 +304,7 @@ const config = {
|
|
|
259
304
|
{
|
|
260
305
|
event: "leave review",
|
|
261
306
|
weight: 2,
|
|
307
|
+
isStrictEvent: false,
|
|
262
308
|
properties: {
|
|
263
309
|
rating: weighNumRange(1, 5),
|
|
264
310
|
wordCount: weighNumRange(10, 200, .25),
|
|
@@ -268,6 +314,7 @@ const config = {
|
|
|
268
314
|
{
|
|
269
315
|
event: "redeem reward",
|
|
270
316
|
weight: 1,
|
|
317
|
+
isStrictEvent: false,
|
|
271
318
|
properties: {
|
|
272
319
|
rewardType: ["discount", "free shipping", "free item", "points bonus"],
|
|
273
320
|
pointsUsed: weighNumRange(100, 5000, .25),
|
|
@@ -375,10 +422,26 @@ const config = {
|
|
|
375
422
|
each key should be an array or function reference
|
|
376
423
|
*/
|
|
377
424
|
userProps: {
|
|
425
|
+
// string
|
|
378
426
|
theme: ["light", "dark", "custom", "light", "dark"],
|
|
379
427
|
title: chance.profession.bind(chance),
|
|
428
|
+
spiritAnimal: ["duck", "dog", "otter", "penguin", "cat", "elephant", "lion", "cheetah", "giraffe", "zebra", "rhino", "hippo", "whale", "dolphin", "shark", "octopus", "squid", "jellyfish", "starfish", "seahorse", "crab", "lobster", "shrimp", "clam", "snail", "slug", "butterfly", "moth", "bee", "wasp", "ant", "beetle", "ladybug", "caterpillar", "centipede", "millipede", "scorpion", "spider", "tarantula", "tick", "mite", "mosquito", "fly", "dragonfly", "damselfly", "grasshopper", "cricket", "locust", "mantis", "cockroach", "termite", "praying mantis", "walking stick", "stick bug", "leaf insect", "lacewing", "aphid", "cicada", "thrips", "psyllid", "scale insect", "whitefly", "mealybug", "planthopper", "leafhopper", "treehopper", "flea", "louse", "bedbug", "flea beetle", "weevil", "longhorn beetle", "leaf beetle", "tiger beetle", "ground beetle", "lady beetle", "firefly", "click beetle", "rove beetle", "scarab beetle", "dung beetle", "stag beetle", "rhinoceros beetle", "hercules beetle", "goliath beetle", "jewel beetle", "tortoise beetle"],
|
|
429
|
+
// numeric
|
|
380
430
|
luckyNumber: weighNumRange(42, 420, .3),
|
|
381
|
-
|
|
431
|
+
// boolean
|
|
432
|
+
emailOptIn: [true, true, false],
|
|
433
|
+
// date
|
|
434
|
+
signupDate: dateRange(),
|
|
435
|
+
// list
|
|
436
|
+
favoriteCategories: listOf(itemCategories, { min: 1, max: 4 }),
|
|
437
|
+
// object
|
|
438
|
+
preferences: { notifications: true, currency: "USD", language: "en-US" },
|
|
439
|
+
// list of objects
|
|
440
|
+
recentOrders: objectList({
|
|
441
|
+
orderId: weighNumRange(100000, 999999),
|
|
442
|
+
total: weighNumRange(10, 500, .25),
|
|
443
|
+
itemCount: [1, 1, 2, 2, 3],
|
|
444
|
+
}, { min: 0, max: 3 }),
|
|
382
445
|
},
|
|
383
446
|
scdProps: {},
|
|
384
447
|
mirrorProps: {},
|
|
@@ -390,17 +453,8 @@ const config = {
|
|
|
390
453
|
groupKeys: [],
|
|
391
454
|
groupProps: {},
|
|
392
455
|
lookupTables: [],
|
|
393
|
-
hook
|
|
394
|
-
|
|
395
|
-
if (type === "everything") {
|
|
396
|
-
const profile = meta.profile;
|
|
397
|
-
record.forEach(e => {
|
|
398
|
-
e.theme = profile.theme;
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return record;
|
|
403
|
-
}
|
|
456
|
+
// No hook by design — this is the engine-validation baseline. Engine + TimeSoup
|
|
457
|
+
// alone produce the trend shape. To experiment with hooks, fork this file.
|
|
404
458
|
};
|
|
405
459
|
|
|
406
460
|
|
|
@@ -3,7 +3,7 @@ const SEED = "SUPER DUPER DANGEROUS BROOO";
|
|
|
3
3
|
const num_days = 92;
|
|
4
4
|
const num_users = 8_000;
|
|
5
5
|
const avg_events_per_user_per_day = 1.3;
|
|
6
|
-
let token = "
|
|
6
|
+
let token = "";
|
|
7
7
|
|
|
8
8
|
// ── env overrides ──
|
|
9
9
|
if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
|
|
@@ -390,6 +390,7 @@ const config = {
|
|
|
390
390
|
{
|
|
391
391
|
event: "api call",
|
|
392
392
|
weight: 10,
|
|
393
|
+
isStrictEvent: false,
|
|
393
394
|
properties: {
|
|
394
395
|
model: ["sonnet-4", "sonnet-4", "sonnet-4", "haiku-4", "haiku-4", "opus-4-6"],
|
|
395
396
|
input_tokens: u.weighNumRange(50, 8000, 0.4, 2000),
|
|
@@ -410,6 +411,7 @@ const config = {
|
|
|
410
411
|
{
|
|
411
412
|
event: "tool use call",
|
|
412
413
|
weight: 4,
|
|
414
|
+
isStrictEvent: false,
|
|
413
415
|
properties: {
|
|
414
416
|
tool_name: ["web_search", "code_interpreter", "file_reader", "calculator", "database_query", "api_connector"],
|
|
415
417
|
execution_time_ms: u.weighNumRange(50, 10000, 0.4, 800),
|
|
@@ -441,6 +443,7 @@ const config = {
|
|
|
441
443
|
{
|
|
442
444
|
event: "eval job",
|
|
443
445
|
weight: 3,
|
|
446
|
+
isStrictEvent: false,
|
|
444
447
|
properties: {
|
|
445
448
|
eval_type: ["accuracy", "relevance", "safety", "latency", "cost", "custom"],
|
|
446
449
|
num_test_cases: u.weighNumRange(10, 1000, 0.3, 100),
|
|
@@ -472,6 +475,7 @@ const config = {
|
|
|
472
475
|
{
|
|
473
476
|
event: "billing payment",
|
|
474
477
|
weight: 2,
|
|
478
|
+
isStrictEvent: false,
|
|
475
479
|
properties: {
|
|
476
480
|
amount_usd: u.weighNumRange(5, 50000, 0.2, 500),
|
|
477
481
|
payment_method: ["credit_card", "credit_card", "credit_card", "invoice", "wire_transfer"],
|
|
@@ -381,6 +381,7 @@ const config = {
|
|
|
381
381
|
{
|
|
382
382
|
event: "article viewed",
|
|
383
383
|
weight: 9,
|
|
384
|
+
isStrictEvent: false,
|
|
384
385
|
properties: {
|
|
385
386
|
wiki_id: chance.pickone.bind(chance, wikiIds),
|
|
386
387
|
content_hub: ["gaming", "anime", "movies", "tv", "comics", "music"],
|
|
@@ -391,6 +392,7 @@ const config = {
|
|
|
391
392
|
{
|
|
392
393
|
event: "article published",
|
|
393
394
|
weight: 3,
|
|
395
|
+
isStrictEvent: false,
|
|
394
396
|
properties: {
|
|
395
397
|
wiki_id: chance.pickone.bind(chance, wikiIds),
|
|
396
398
|
content_hub: ["gaming", "anime", "movies", "tv", "comics", "music"],
|
|
@@ -413,6 +415,7 @@ const config = {
|
|
|
413
415
|
{
|
|
414
416
|
event: "discussion posted",
|
|
415
417
|
weight: 5,
|
|
418
|
+
isStrictEvent: false,
|
|
416
419
|
properties: {
|
|
417
420
|
community_id: chance.pickone.bind(chance, communityIds),
|
|
418
421
|
content_hub: ["gaming", "anime", "movies", "tv", "comics", "music"],
|
|
@@ -424,6 +427,7 @@ const config = {
|
|
|
424
427
|
{
|
|
425
428
|
event: "comment posted",
|
|
426
429
|
weight: 6,
|
|
430
|
+
isStrictEvent: false,
|
|
427
431
|
properties: {
|
|
428
432
|
community_id: chance.pickone.bind(chance, communityIds),
|
|
429
433
|
content_hub: ["gaming", "anime", "movies", "tv", "comics", "music"],
|
|
@@ -435,6 +439,7 @@ const config = {
|
|
|
435
439
|
{
|
|
436
440
|
event: "upvote given",
|
|
437
441
|
weight: 7,
|
|
442
|
+
isStrictEvent: false,
|
|
438
443
|
properties: {
|
|
439
444
|
content_type: ["article", "article", "discussion", "comment"],
|
|
440
445
|
content_hub: ["gaming", "anime", "movies", "tv", "comics", "music"],
|
|
@@ -565,6 +570,7 @@ const config = {
|
|
|
565
570
|
order: "sequential",
|
|
566
571
|
timeToConvert: 96,
|
|
567
572
|
weight: 4,
|
|
573
|
+
reentry: true,
|
|
568
574
|
},
|
|
569
575
|
{
|
|
570
576
|
name: "Creator to Supporter",
|
|
@@ -837,11 +843,11 @@ const config = {
|
|
|
837
843
|
}
|
|
838
844
|
|
|
839
845
|
// -- HOOK 8: PRO SUBSCRIBER CONTENT CREATION LIFT ---------
|
|
840
|
-
// Free-tier users drop
|
|
841
|
-
//
|
|
846
|
+
// Free-tier users drop 65% of comment events to widen the funnel
|
|
847
|
+
// conversion gap to ~2x vs paid subscribers.
|
|
842
848
|
if (profile.subscription_tier !== "pro" && profile.subscription_tier !== "supporter") {
|
|
843
849
|
events = events.filter(e => {
|
|
844
|
-
if (e.event === "comment posted" && chance.bool({ likelihood:
|
|
850
|
+
if (e.event === "comment posted" && chance.bool({ likelihood: 65 })) return false;
|
|
845
851
|
return true;
|
|
846
852
|
});
|
|
847
853
|
}
|
|
@@ -426,6 +426,7 @@ const config = {
|
|
|
426
426
|
{
|
|
427
427
|
event: "deposit",
|
|
428
428
|
weight: 5,
|
|
429
|
+
isStrictEvent: false,
|
|
429
430
|
properties: {
|
|
430
431
|
token: ["ETH", "USDC", "USDT", "SOL", "BTC"],
|
|
431
432
|
deposit_amount_usd: u.weighNumRange(10, 5000, 0.3, 500),
|
|
@@ -436,6 +437,7 @@ const config = {
|
|
|
436
437
|
{
|
|
437
438
|
event: "withdrawal",
|
|
438
439
|
weight: 3,
|
|
440
|
+
isStrictEvent: false,
|
|
439
441
|
properties: {
|
|
440
442
|
token: ["ETH", "USDC", "USDT", "SOL", "BTC"],
|
|
441
443
|
amount_usd: u.weighNumRange(10, 5000, 0.3, 400),
|
|
@@ -446,6 +448,7 @@ const config = {
|
|
|
446
448
|
{
|
|
447
449
|
event: "swap",
|
|
448
450
|
weight: 10,
|
|
451
|
+
isStrictEvent: false,
|
|
449
452
|
properties: {
|
|
450
453
|
token_pair: TOKEN_PAIRS,
|
|
451
454
|
trade_amount_usd: u.weighNumRange(10, 10000, 0.3, 200),
|
|
@@ -458,6 +461,7 @@ const config = {
|
|
|
458
461
|
{
|
|
459
462
|
event: "stake",
|
|
460
463
|
weight: 4,
|
|
464
|
+
isStrictEvent: false,
|
|
461
465
|
properties: {
|
|
462
466
|
token: ["ETH", "SOL", "MATIC", "AVAX", "ATOM", "DOT"],
|
|
463
467
|
amount_usd: u.weighNumRange(50, 10000, 0.3, 500),
|
|
@@ -478,6 +482,7 @@ const config = {
|
|
|
478
482
|
{
|
|
479
483
|
event: "claim airdrop",
|
|
480
484
|
weight: 2,
|
|
485
|
+
isStrictEvent: false,
|
|
481
486
|
properties: {
|
|
482
487
|
airdrop_name: AIRDROP_NAMES,
|
|
483
488
|
token_amount: u.weighNumRange(10, 10000, 0.3, 500),
|
|
@@ -277,6 +277,7 @@ const config = {
|
|
|
277
277
|
{
|
|
278
278
|
event: "photo uploaded",
|
|
279
279
|
weight: 12,
|
|
280
|
+
isStrictEvent: false,
|
|
280
281
|
properties: {
|
|
281
282
|
photo_number: u.weighNumRange(1, 6, 0.5, 2),
|
|
282
283
|
has_face: [true, true, true, true, false],
|
|
@@ -300,6 +301,7 @@ const config = {
|
|
|
300
301
|
{
|
|
301
302
|
event: "swipe right",
|
|
302
303
|
weight: 10,
|
|
304
|
+
isStrictEvent: false,
|
|
303
305
|
properties: {
|
|
304
306
|
is_super_like: [false, false, false, false, false, false, false, false, false, true],
|
|
305
307
|
swipe_source: ["feed", "feed", "feed", "discover", "boost", "nearby"],
|
|
@@ -315,6 +317,7 @@ const config = {
|
|
|
315
317
|
{
|
|
316
318
|
event: "match received",
|
|
317
319
|
weight: 4,
|
|
320
|
+
isStrictEvent: false,
|
|
318
321
|
properties: {
|
|
319
322
|
match_score: u.weighNumRange(50, 100, 0.5, 75),
|
|
320
323
|
},
|
|
@@ -322,6 +325,7 @@ const config = {
|
|
|
322
325
|
{
|
|
323
326
|
event: "message sent",
|
|
324
327
|
weight: 6,
|
|
328
|
+
isStrictEvent: false,
|
|
325
329
|
properties: {
|
|
326
330
|
message_length: u.weighNumRange(1, 500, 0.3, 40),
|
|
327
331
|
has_emoji: [false, false, true, true, true],
|
|
@@ -338,6 +342,7 @@ const config = {
|
|
|
338
342
|
{
|
|
339
343
|
event: "phone number exchanged",
|
|
340
344
|
weight: 1,
|
|
345
|
+
isStrictEvent: false,
|
|
341
346
|
properties: {
|
|
342
347
|
exchange_method: ["in_chat", "in_chat", "voice_call", "video_call"],
|
|
343
348
|
},
|
|
@@ -345,6 +350,7 @@ const config = {
|
|
|
345
350
|
{
|
|
346
351
|
event: "date scheduled",
|
|
347
352
|
weight: 1,
|
|
353
|
+
isStrictEvent: false,
|
|
348
354
|
properties: {
|
|
349
355
|
venue_type: ["coffee", "dinner", "drinks", "activity", "virtual"],
|
|
350
356
|
},
|
|
@@ -359,6 +365,7 @@ const config = {
|
|
|
359
365
|
{
|
|
360
366
|
event: "premium upgrade",
|
|
361
367
|
weight: 1,
|
|
368
|
+
isStrictEvent: false,
|
|
362
369
|
properties: {
|
|
363
370
|
plan: ["Premium", "Premium", "Premium", "Elite"],
|
|
364
371
|
price_usd: [14.99, 14.99, 14.99, 29.99],
|
|
@@ -392,6 +399,7 @@ const config = {
|
|
|
392
399
|
{
|
|
393
400
|
event: "app opened",
|
|
394
401
|
weight: 8,
|
|
402
|
+
isStrictEvent: false,
|
|
395
403
|
properties: {
|
|
396
404
|
session_duration_mins: u.weighNumRange(1, 120, 0.3, 8),
|
|
397
405
|
},
|
|
@@ -416,6 +424,7 @@ const config = {
|
|
|
416
424
|
order: "sequential",
|
|
417
425
|
timeToConvert: 24,
|
|
418
426
|
weight: 6,
|
|
427
|
+
reentry: true,
|
|
419
428
|
},
|
|
420
429
|
{
|
|
421
430
|
name: "Date Funnel",
|
|
@@ -424,6 +433,7 @@ const config = {
|
|
|
424
433
|
order: "sequential",
|
|
425
434
|
timeToConvert: 72,
|
|
426
435
|
weight: 3,
|
|
436
|
+
reentry: true,
|
|
427
437
|
},
|
|
428
438
|
{
|
|
429
439
|
name: "Monetization",
|
|
@@ -555,10 +565,9 @@ const config = {
|
|
|
555
565
|
});
|
|
556
566
|
}
|
|
557
567
|
|
|
558
|
-
// HOOK 1
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
// (over-curated profile reads as fake/staged).
|
|
568
|
+
// HOOK 1: PHOTO MAGIC NUMBER (sweet 2-5 photos → clone 2-4 extra matches)
|
|
569
|
+
// Over-6 score reduction is applied AT THE END of this hook so it also
|
|
570
|
+
// affects matches injected by HOOK 4 (premium boost).
|
|
562
571
|
if (photoUploadCount >= 2 && photoUploadCount <= 5 && matchEvents.length > 0) {
|
|
563
572
|
const matchTemplate = matchEvents[0];
|
|
564
573
|
matchEvents.forEach(m => {
|
|
@@ -572,12 +581,6 @@ const config = {
|
|
|
572
581
|
});
|
|
573
582
|
}
|
|
574
583
|
});
|
|
575
|
-
} else if (photoUploadCount >= 6) {
|
|
576
|
-
events.forEach(e => {
|
|
577
|
-
if (e.event === "match received" && typeof e.match_score === "number") {
|
|
578
|
-
e.match_score = Math.max(20, Math.round(e.match_score * 0.65));
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
584
|
}
|
|
582
585
|
|
|
583
586
|
// HOOK 2: WEEKEND SWIPE SURGE — Sunday swipes get heavy cloning
|
|
@@ -742,6 +745,16 @@ const config = {
|
|
|
742
745
|
});
|
|
743
746
|
}
|
|
744
747
|
|
|
748
|
+
// HOOK 1b: PHOTO MAGIC NUMBER — over-6 score reduction (applied LAST so
|
|
749
|
+
// it also affects matches injected by HOOK 4 premium boost).
|
|
750
|
+
if (photoUploadCount >= 6) {
|
|
751
|
+
events.forEach(e => {
|
|
752
|
+
if (e.event === "match received" && typeof e.match_score === "number") {
|
|
753
|
+
e.match_score = Math.max(20, Math.round(e.match_score * 0.65));
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
|
|
745
758
|
// HOOK 8: OFF-APP RETENTION — users with phone-exchanged or
|
|
746
759
|
// date-scheduled in first 14 days get extra cloned app-open + swipe
|
|
747
760
|
// events past day 30. Non-milestone users lose 80% of post-day-30
|
|
@@ -356,6 +356,7 @@ const config = {
|
|
|
356
356
|
{
|
|
357
357
|
event: "build completed",
|
|
358
358
|
weight: 8,
|
|
359
|
+
isStrictEvent: false,
|
|
359
360
|
properties: {
|
|
360
361
|
pipeline_id: chance.pickone.bind(chance, pipelineIds),
|
|
361
362
|
repo_id: chance.pickone.bind(chance, repoIds),
|
|
@@ -369,6 +370,7 @@ const config = {
|
|
|
369
370
|
{
|
|
370
371
|
event: "deployment completed",
|
|
371
372
|
weight: 5,
|
|
373
|
+
isStrictEvent: false,
|
|
372
374
|
properties: {
|
|
373
375
|
pipeline_id: chance.pickone.bind(chance, pipelineIds),
|
|
374
376
|
repo_id: chance.pickone.bind(chance, repoIds),
|
|
@@ -381,6 +383,7 @@ const config = {
|
|
|
381
383
|
{
|
|
382
384
|
event: "pull request created",
|
|
383
385
|
weight: 6,
|
|
386
|
+
isStrictEvent: false,
|
|
384
387
|
properties: {
|
|
385
388
|
repo_id: chance.pickone.bind(chance, repoIds),
|
|
386
389
|
pr_size: ["small", "small", "medium", "medium", "large", "xlarge"],
|
|
@@ -393,6 +396,7 @@ const config = {
|
|
|
393
396
|
{
|
|
394
397
|
event: "code review completed",
|
|
395
398
|
weight: 5,
|
|
399
|
+
isStrictEvent: false,
|
|
396
400
|
properties: {
|
|
397
401
|
repo_id: chance.pickone.bind(chance, repoIds),
|
|
398
402
|
review_result: ["approved", "approved", "approved", "changes_requested", "commented"],
|
|
@@ -404,6 +408,7 @@ const config = {
|
|
|
404
408
|
{
|
|
405
409
|
event: "alert triggered",
|
|
406
410
|
weight: 4,
|
|
411
|
+
isStrictEvent: false,
|
|
407
412
|
properties: {
|
|
408
413
|
alert_type: ["error_rate", "latency", "cpu", "memory", "disk", "custom_metric"],
|
|
409
414
|
severity: ["info", "warning", "warning", "critical", "critical"],
|
|
@@ -414,6 +419,7 @@ const config = {
|
|
|
414
419
|
{
|
|
415
420
|
event: "incident created",
|
|
416
421
|
weight: 2,
|
|
422
|
+
isStrictEvent: false,
|
|
417
423
|
properties: {
|
|
418
424
|
severity: ["sev1", "sev2", "sev2", "sev3", "sev3", "sev3"],
|
|
419
425
|
service: ["api", "web", "worker", "database", "cdn", "auth"],
|
|
@@ -424,6 +430,7 @@ const config = {
|
|
|
424
430
|
{
|
|
425
431
|
event: "incident resolved",
|
|
426
432
|
weight: 2,
|
|
433
|
+
isStrictEvent: false,
|
|
427
434
|
properties: {
|
|
428
435
|
severity: ["sev1", "sev2", "sev2", "sev3", "sev3", "sev3"],
|
|
429
436
|
resolution_time_minutes: u.weighNumRange(5, 480, 0.3, 60),
|
|
@@ -461,6 +468,7 @@ const config = {
|
|
|
461
468
|
{
|
|
462
469
|
event: "monitoring dashboard viewed",
|
|
463
470
|
weight: 5,
|
|
471
|
+
isStrictEvent: false,
|
|
464
472
|
properties: {
|
|
465
473
|
dashboard_type: ["overview", "performance", "errors", "deploys", "custom"],
|
|
466
474
|
time_range: ["1h", "6h", "24h", "7d", "30d"],
|
|
@@ -541,6 +549,7 @@ const config = {
|
|
|
541
549
|
order: "sequential",
|
|
542
550
|
timeToConvert: 48,
|
|
543
551
|
weight: 5,
|
|
552
|
+
reentry: true,
|
|
544
553
|
},
|
|
545
554
|
{
|
|
546
555
|
name: "PR Review Flow",
|
|
@@ -557,6 +566,7 @@ const config = {
|
|
|
557
566
|
order: "sequential",
|
|
558
567
|
timeToConvert: 24,
|
|
559
568
|
weight: 3,
|
|
569
|
+
reentry: true,
|
|
560
570
|
},
|
|
561
571
|
{
|
|
562
572
|
name: "Upgrade Path",
|