@ak--47/dungeon-master 1.5.4 → 1.6.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 +21 -12
- package/.claude/skills/create-dungeon/SKILL.md +65 -5
- package/.claude/skills/create-project/SKILL.md +1 -1
- package/.claude/skills/create-project/provision.mjs +57 -10
- package/.claude/skills/verify-dungeon/SKILL.md +56 -25
- package/.claude/skills/verify-dungeon/references/counting-semantics.md +26 -6
- package/.claude/skills/verify-dungeon/references/report-format.md +14 -8
- package/.claude/skills/write-hooks/SKILL.md +156 -18
- package/CHANGELOG.md +183 -0
- package/HOOKS.md +566 -112
- package/README.md +28 -9
- package/docs/guides/1.3.0-upgrade-guide.md +262 -0
- package/docs/guides/1.3.1-upgrade-guide.md +328 -0
- package/docs/guides/1.4.0-upgrade-guide.md +130 -0
- package/docs/guides/1.4.1-upgrade-guide.md +125 -0
- package/docs/guides/1.4.3-upgrade-guide.md +200 -0
- package/docs/guides/1.4.4-upgrade-guide.md +95 -0
- package/docs/guides/1.5.0-upgrade-guide.md +426 -0
- package/docs/guides/1.5.1-upgrade-guide.md +178 -0
- package/docs/guides/1.5.3-upgrade-guide.md +117 -0
- package/docs/guides/1.6.0-upgrade-guide.md +204 -0
- package/dungeons/technical/stories-verify.js +152 -0
- package/dungeons/vertical/README.md +154 -0
- package/dungeons/vertical/ai-platform/ai-platform.js +1424 -0
- package/dungeons/vertical/ai-platform/ai-platform.sql +184 -0
- package/dungeons/vertical/ai-platform/ai-platform.verify.mjs +71 -0
- package/dungeons/vertical/community/community.js +1589 -0
- package/dungeons/vertical/community/community.sql +216 -0
- package/dungeons/vertical/community/community.verify.mjs +72 -0
- package/dungeons/vertical/crypto/crypto.js +1483 -0
- package/dungeons/vertical/crypto/crypto.sql +194 -0
- package/dungeons/vertical/crypto/crypto.verify.mjs +78 -0
- package/dungeons/vertical/dating/dating.js +1540 -0
- package/dungeons/vertical/dating/dating.sql +288 -0
- package/dungeons/vertical/dating/dating.verify.mjs +72 -0
- package/dungeons/vertical/devtools/devtools.js +1524 -0
- package/dungeons/vertical/devtools/devtools.sql +161 -0
- package/dungeons/vertical/devtools/devtools.verify.mjs +73 -0
- package/dungeons/vertical/ecommerce/ecommerce.js +1480 -0
- package/dungeons/vertical/ecommerce/ecommerce.sql +218 -0
- package/dungeons/vertical/ecommerce/ecommerce.verify.mjs +70 -0
- package/dungeons/vertical/education/education.js +1763 -0
- package/dungeons/vertical/education/education.sql +243 -0
- package/dungeons/vertical/education/education.verify.mjs +75 -0
- package/dungeons/vertical/fintech/fintech.js +1410 -0
- package/dungeons/vertical/fintech/fintech.sql +175 -0
- package/dungeons/vertical/fintech/fintech.verify.mjs +70 -0
- package/dungeons/vertical/fitness/fitness.js +1495 -0
- package/dungeons/vertical/fitness/fitness.sql +206 -0
- package/dungeons/vertical/fitness/fitness.verify.mjs +71 -0
- package/dungeons/vertical/food-delivery/food-delivery.js +1641 -0
- package/dungeons/vertical/food-delivery/food-delivery.sql +183 -0
- package/dungeons/vertical/food-delivery/food-delivery.verify.mjs +75 -0
- package/dungeons/vertical/gaming/gaming.js +1741 -0
- package/dungeons/vertical/gaming/gaming.sql +249 -0
- package/dungeons/vertical/gaming/gaming.verify.mjs +70 -0
- package/dungeons/vertical/healthcare/healthcare.js +1488 -0
- package/dungeons/vertical/healthcare/healthcare.sql +193 -0
- package/dungeons/vertical/healthcare/healthcare.verify.mjs +72 -0
- package/dungeons/vertical/insurance-application/insurance-application.js +1852 -0
- package/dungeons/vertical/insurance-application/insurance-application.sql +174 -0
- package/dungeons/vertical/insurance-application/insurance-application.verify.mjs +74 -0
- package/dungeons/vertical/logistics/logistics.js +1541 -0
- package/dungeons/vertical/logistics/logistics.sql +205 -0
- package/dungeons/vertical/logistics/logistics.verify.mjs +73 -0
- package/dungeons/vertical/marketplace/marketplace.js +1438 -0
- package/dungeons/vertical/marketplace/marketplace.sql +148 -0
- package/dungeons/vertical/marketplace/marketplace.verify.mjs +74 -0
- package/dungeons/vertical/{media.js → media/media.js} +557 -58
- package/dungeons/vertical/media/media.sql +173 -0
- package/dungeons/vertical/media/media.verify.mjs +74 -0
- package/dungeons/vertical/real-estate/real-estate.js +1629 -0
- package/dungeons/vertical/real-estate/real-estate.sql +167 -0
- package/dungeons/vertical/real-estate/real-estate.verify.mjs +75 -0
- package/dungeons/vertical/sass/sass.js +1733 -0
- package/dungeons/vertical/sass/sass.sql +173 -0
- package/dungeons/vertical/sass/sass.verify.mjs +74 -0
- package/dungeons/vertical/social/social.js +1456 -0
- package/dungeons/vertical/social/social.sql +160 -0
- package/dungeons/vertical/social/social.verify.mjs +76 -0
- package/dungeons/vertical/streaming/streaming.js +677 -0
- package/dungeons/vertical/streaming/streaming.sql +101 -0
- package/dungeons/vertical/streaming/streaming.verify.mjs +75 -0
- package/dungeons/vertical/support-desk/support-desk.js +741 -0
- package/dungeons/vertical/support-desk/support-desk.sql +137 -0
- package/dungeons/vertical/support-desk/support-desk.verify.mjs +75 -0
- package/dungeons/vertical/travel/travel.js +1603 -0
- package/dungeons/vertical/travel/travel.sql +236 -0
- package/dungeons/vertical/travel/travel.verify.mjs +72 -0
- package/index.js +2 -2
- package/lib/core/config-validator.js +37 -2
- package/lib/core/context.js +4 -2
- package/lib/core/dungeon-loader.js +20 -15
- package/lib/core/dungeon-to-json.js +22 -4
- package/lib/core/storage.js +3 -3
- package/lib/generators/funnels.js +36 -9
- package/lib/hook-helpers/cohort.js +53 -0
- package/lib/hook-helpers/index.js +8 -0
- package/lib/hook-helpers/mutate.js +1 -1
- package/lib/hook-helpers/shape.js +319 -0
- package/lib/hook-patterns/aggregate-per-user-by-bin.js +13 -5
- package/lib/hook-patterns/attributed-by-source.js +60 -58
- package/lib/hook-patterns/frequency-by-frequency.js +14 -4
- package/lib/hook-patterns/funnel-frequency-breakdown.js +24 -12
- package/lib/hook-patterns/index.js +1 -1
- package/lib/hook-patterns/time-to-convert-by-segment.js +75 -13
- package/lib/orchestrators/user-loop.js +41 -0
- package/lib/templates/story-spec.schema.json +175 -0
- package/lib/utils/json-evaluator.js +1 -1
- package/lib/utils/utils.js +5 -4
- package/lib/verify/coerce.js +186 -0
- package/lib/verify/counting.js +270 -32
- package/lib/verify/emulate-breakdown.js +1285 -203
- package/lib/verify/first-time.js +89 -0
- package/lib/verify/flows.js +679 -0
- package/lib/verify/formula.js +259 -0
- package/lib/verify/funnel-engine.js +697 -113
- package/lib/verify/index.js +32 -4
- package/lib/verify/sessionize.js +275 -0
- package/lib/verify/story-runner.js +483 -0
- package/lib/verify/verify-dungeon.js +59 -37
- package/package.json +2 -1
- package/scripts/extract-dungeon-schema.mjs +2 -2
- package/scripts/run-many.mjs +2 -2
- package/scripts/verify-runner.mjs +2 -2
- package/scripts/verify-stories.mjs +244 -0
- package/types.d.ts +296 -30
- package/dungeons/vertical/ai-platform.js +0 -818
- package/dungeons/vertical/community.js +0 -966
- package/dungeons/vertical/crypto.js +0 -895
- package/dungeons/vertical/dating.js +0 -830
- package/dungeons/vertical/devtools.js +0 -1050
- package/dungeons/vertical/ecommerce.js +0 -811
- package/dungeons/vertical/education.js +0 -964
- package/dungeons/vertical/fintech.js +0 -946
- package/dungeons/vertical/fitness.js +0 -935
- package/dungeons/vertical/food-delivery.js +0 -934
- package/dungeons/vertical/gaming.js +0 -1120
- package/dungeons/vertical/healthcare.js +0 -945
- package/dungeons/vertical/insurance-application.js +0 -1039
- package/dungeons/vertical/logistics.js +0 -986
- package/dungeons/vertical/marketplace.js +0 -1019
- package/dungeons/vertical/real-estate.js +0 -872
- package/dungeons/vertical/sass.js +0 -995
- package/dungeons/vertical/social.js +0 -904
- package/dungeons/vertical/travel.js +0 -930
|
@@ -1,1019 +0,0 @@
|
|
|
1
|
-
// ── IMPORTS ──
|
|
2
|
-
import dayjs from "dayjs";
|
|
3
|
-
import utc from "dayjs/plugin/utc.js";
|
|
4
|
-
dayjs.extend(utc);
|
|
5
|
-
import "dotenv/config";
|
|
6
|
-
import * as u from "../../lib/utils/utils.js";
|
|
7
|
-
import * as v from "ak-tools";
|
|
8
|
-
/** @typedef {import("../../types").Dungeon} Config */
|
|
9
|
-
|
|
10
|
-
// ── OVERVIEW ──
|
|
11
|
-
/*
|
|
12
|
-
* NAME: TradeNest
|
|
13
|
-
* APP: Two-sided marketplace connecting sellers who list products with
|
|
14
|
-
* buyers who search, purchase, and review. Sellers list items;
|
|
15
|
-
* buyers browse, message, negotiate, and buy. Revenue from
|
|
16
|
-
* marketplace fees and listing fees.
|
|
17
|
-
* SCALE: 10,000 users, ~1.4M events, 121 days (2026-01-01 → 2026-05-01)
|
|
18
|
-
* CORE LOOP: search → view → add to cart → purchase → review
|
|
19
|
-
* (seller side: create listing → receive offers → accept → ship)
|
|
20
|
-
*
|
|
21
|
-
* EVENTS (18):
|
|
22
|
-
* item searched (8) > app session (8) > item viewed (7) > notification received (6)
|
|
23
|
-
* > add to cart (5) > message sent (5) > listing created (4) > purchase completed (3)
|
|
24
|
-
* > listing updated (3) > offer received (3) > shipping updated (3) > offer accepted (2)
|
|
25
|
-
* > review submitted (2) > seller rated (2) > profile updated (2) > account created (1)
|
|
26
|
-
* > refund requested (1) > account deactivated (1)
|
|
27
|
-
*
|
|
28
|
-
* FUNNELS (5):
|
|
29
|
-
* - Buyer Onboarding: account created → item searched → item viewed → add to cart (40%)
|
|
30
|
-
* - Browse to Purchase: item searched → item viewed → add to cart → purchase completed (30%)
|
|
31
|
-
* - Seller Listing Flow: listing created → listing updated → offer received → offer accepted (25%)
|
|
32
|
-
* - Offer Negotiation: offer received → message sent → offer accepted (35%)
|
|
33
|
-
* - Review After Purchase: purchase completed → shipping updated → review submitted (20%)
|
|
34
|
-
*
|
|
35
|
-
* USER PROPS: user_type, segment, seller_rating, total_transactions, response_time_hours,
|
|
36
|
-
* store_name, Platform, category
|
|
37
|
-
* SUPER PROPS: Platform, category
|
|
38
|
-
* SCD PROPS: seller_tier (new/verified/power/featured, monthly fuzzy, max 8)
|
|
39
|
-
* GROUPS: none
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
// ── HOOK STORIES ──
|
|
43
|
-
/*
|
|
44
|
-
* NOTE: All cohort effects are HIDDEN — no flag stamping. Discoverable via
|
|
45
|
-
* raw-prop breakdowns (segment, day, category) or behavioral cohorts.
|
|
46
|
-
*
|
|
47
|
-
* ───────────────────────────────────────────────────────────────
|
|
48
|
-
* 1. FEE CHANGE IMPACT (everything hook)
|
|
49
|
-
* ───────────────────────────────────────────────────────────────
|
|
50
|
-
*
|
|
51
|
-
* PATTERN: After day 45 (permanent marketplace fee increase), all
|
|
52
|
-
* "listing created" events get listing_fee multiplied by 1.3x.
|
|
53
|
-
* Simulates the revenue impact of a platform fee adjustment. No flag —
|
|
54
|
-
* discover via line chart of avg listing_fee over time.
|
|
55
|
-
*
|
|
56
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
57
|
-
*
|
|
58
|
-
* Report 1: Listing Fee Over Time
|
|
59
|
-
* • Report type: Insights
|
|
60
|
-
* • Event: "listing created"
|
|
61
|
-
* • Measure: Average of "listing_fee"
|
|
62
|
-
* • Line chart by week
|
|
63
|
-
* • Expected: Clear step-up around day 45 from ~$15 avg to ~$20 avg
|
|
64
|
-
*
|
|
65
|
-
* REAL-WORLD ANALOGUE: Marketplace platforms periodically adjust
|
|
66
|
-
* commission/listing fees, impacting seller economics and behavior.
|
|
67
|
-
*
|
|
68
|
-
* ───────────────────────────────────────────────────────────────
|
|
69
|
-
* 2. WEEKEND SHOPPING SURGE (event hook)
|
|
70
|
-
* ───────────────────────────────────────────────────────────────
|
|
71
|
-
*
|
|
72
|
-
* PATTERN: "purchase completed" events on Saturday or Sunday get
|
|
73
|
-
* total_amount multiplied by 1.2x. Weekend shoppers spend more
|
|
74
|
-
* per transaction due to leisure browsing.
|
|
75
|
-
*
|
|
76
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
77
|
-
*
|
|
78
|
-
* Report 1: Weekend vs Weekday Average Spend
|
|
79
|
-
* • Report type: Insights
|
|
80
|
-
* • Event: "purchase completed"
|
|
81
|
-
* • Measure: Average of "total_amount"
|
|
82
|
-
* • Breakdown: Day of week
|
|
83
|
-
* • Expected: Sat/Sun avg ≈ $72 vs Mon-Fri avg ≈ $60
|
|
84
|
-
*
|
|
85
|
-
* REAL-WORLD ANALOGUE: E-commerce platforms see higher average
|
|
86
|
-
* order values on weekends when buyers browse leisurely.
|
|
87
|
-
*
|
|
88
|
-
* ───────────────────────────────────────────────────────────────
|
|
89
|
-
* 3. SELLER SUCCESS → BUYER TRUST (everything hook)
|
|
90
|
-
* ───────────────────────────────────────────────────────────────
|
|
91
|
-
*
|
|
92
|
-
* PATTERN: Users with user_type "seller" and segment "power_seller"
|
|
93
|
-
* (from meta.profile) get their "purchase completed" events cloned
|
|
94
|
-
* 2x. Power sellers attract more transactions due to trust/reputation.
|
|
95
|
-
*
|
|
96
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
97
|
-
*
|
|
98
|
-
* Report 1: Purchases per User by Segment
|
|
99
|
-
* • Report type: Insights
|
|
100
|
-
* • Event: "purchase completed"
|
|
101
|
-
* • Measure: Total per user
|
|
102
|
-
* • Breakdown: user property "segment"
|
|
103
|
-
* • Expected: power_seller ≈ 2x purchases vs casual_seller
|
|
104
|
-
*
|
|
105
|
-
* REAL-WORLD ANALOGUE: High-rated sellers with established reputations
|
|
106
|
-
* receive disproportionately more sales on platforms like eBay/Etsy.
|
|
107
|
-
*
|
|
108
|
-
* ───────────────────────────────────────────────────────────────
|
|
109
|
-
* 4. SEARCH-TO-PURCHASE BY CATEGORY (everything hook)
|
|
110
|
-
* ───────────────────────────────────────────────────────────────
|
|
111
|
-
*
|
|
112
|
-
* PATTERN: Users whose events include "item searched" with category
|
|
113
|
-
* "electronics" get cloned "purchase completed" events (electronics
|
|
114
|
-
* buyers have higher conversion). Creates category-specific lift.
|
|
115
|
-
*
|
|
116
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
117
|
-
*
|
|
118
|
-
* Report 1: Purchase Volume by Category
|
|
119
|
-
* • Report type: Insights
|
|
120
|
-
* • Event: "purchase completed"
|
|
121
|
-
* • Measure: Total
|
|
122
|
-
* • Breakdown: "category" (superProp)
|
|
123
|
-
* • Expected: electronics ≈ 1.5-2x more purchases than clothing/home
|
|
124
|
-
*
|
|
125
|
-
* REAL-WORLD ANALOGUE: Electronics shoppers have higher purchase
|
|
126
|
-
* intent — they research, decide, and buy with less browsing.
|
|
127
|
-
*
|
|
128
|
-
* ───────────────────────────────────────────────────────────────
|
|
129
|
-
* 5. RESPONSE TIME → CONVERSION (everything hook)
|
|
130
|
-
* ───────────────────────────────────────────────────────────────
|
|
131
|
-
*
|
|
132
|
-
* PATTERN: Users whose average response_time_hours on "message sent"
|
|
133
|
-
* events is <= 4 hours get additional "offer accepted" events cloned
|
|
134
|
-
* (2x existing + 60% of offer_received). Slow responders (> 4h avg)
|
|
135
|
-
* lose 60% of offer_accepted events. Fast responders close more deals.
|
|
136
|
-
*
|
|
137
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
138
|
-
*
|
|
139
|
-
* Report 1: Offer Accepted Count vs Response Time
|
|
140
|
-
* • Report type: Insights
|
|
141
|
-
* • Event: "offer accepted"
|
|
142
|
-
* • Measure: Total per user
|
|
143
|
-
* • Breakdown: user property "response_time_hours"
|
|
144
|
-
* • Expected: Users with < 2hr avg response ≈ 2x offer accepts
|
|
145
|
-
*
|
|
146
|
-
* REAL-WORLD ANALOGUE: Sellers who respond quickly to inquiries
|
|
147
|
-
* close significantly more deals on marketplace platforms.
|
|
148
|
-
*
|
|
149
|
-
* ───────────────────────────────────────────────────────────────
|
|
150
|
-
* 6. NEW SELLER CHURN (everything hook)
|
|
151
|
-
* ───────────────────────────────────────────────────────────────
|
|
152
|
-
*
|
|
153
|
-
* PATTERN: Users with segment "new_seller" and fewer than 10 total
|
|
154
|
-
* events lose 40% of events after day 14. Simulates new sellers
|
|
155
|
-
* who try the platform and abandon it quickly.
|
|
156
|
-
*
|
|
157
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
158
|
-
*
|
|
159
|
-
* Report 1: New Seller Retention
|
|
160
|
-
* • Report type: Insights
|
|
161
|
-
* • Event: All events
|
|
162
|
-
* • Measure: Total per user
|
|
163
|
-
* • Filter: segment = "new_seller"
|
|
164
|
-
* • Line chart by week
|
|
165
|
-
* • Expected: Sharp drop-off around week 2-3
|
|
166
|
-
*
|
|
167
|
-
* Report 2: Active Users by Segment
|
|
168
|
-
* • Report type: Insights
|
|
169
|
-
* • Event: Any active event
|
|
170
|
-
* • Measure: Uniques
|
|
171
|
-
* • Breakdown: user property "segment"
|
|
172
|
-
* • Expected: new_seller retention ~60% vs power_seller ~99%
|
|
173
|
-
*
|
|
174
|
-
* REAL-WORLD ANALOGUE: Most new sellers on marketplace platforms
|
|
175
|
-
* churn within the first month if they don't get early traction.
|
|
176
|
-
*
|
|
177
|
-
* ───────────────────────────────────────────────────────────────
|
|
178
|
-
* 7. POWER SELLER PROFILES (user hook)
|
|
179
|
-
* ───────────────────────────────────────────────────────────────
|
|
180
|
-
*
|
|
181
|
-
* PATTERN: Users with segment "power_seller" get total_transactions
|
|
182
|
-
* set to 100-500, seller_rating to 4.5-5.0, and store_name to a
|
|
183
|
-
* realistic business name. Enriches profiles for realistic cohort analysis.
|
|
184
|
-
*
|
|
185
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
186
|
-
*
|
|
187
|
-
* Report 1: Transaction Count Distribution by Segment
|
|
188
|
-
* • Report type: Insights
|
|
189
|
-
* • Measure: Profiles → Breakdown by "total_transactions" (histogram)
|
|
190
|
-
* • Filter: segment = "power_seller"
|
|
191
|
-
* • Expected: power_seller total_transactions 100-500, others ≈ 0
|
|
192
|
-
*
|
|
193
|
-
* Report 2: Seller Rating by Segment
|
|
194
|
-
* • Report type: Insights
|
|
195
|
-
* • Measure: Profiles → Average of "seller_rating"
|
|
196
|
-
* • Breakdown: "segment"
|
|
197
|
-
* • Expected: power_seller ≈ 4.7 avg, casual_seller ≈ 3.5
|
|
198
|
-
*
|
|
199
|
-
* REAL-WORLD ANALOGUE: Top sellers have established storefronts,
|
|
200
|
-
* high transaction volumes, and near-perfect ratings.
|
|
201
|
-
*
|
|
202
|
-
* ───────────────────────────────────────────────────────────────
|
|
203
|
-
* 8. FREQUENT BUYER FUNNEL LIFT (everything hook)
|
|
204
|
-
* ───────────────────────────────────────────────────────────────
|
|
205
|
-
*
|
|
206
|
-
* PATTERN: Non-frequent-buyer users lose ~25% of "purchase completed"
|
|
207
|
-
* events (last step of Browse to Purchase funnel), simulating lower
|
|
208
|
-
* conversion. Frequent buyers retain all purchase events.
|
|
209
|
-
*
|
|
210
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
211
|
-
*
|
|
212
|
-
* Report 1: Browse-to-Purchase Conversion by Segment
|
|
213
|
-
* • Report type: Funnels
|
|
214
|
-
* • Steps: "item searched" → "item viewed" → "add to cart" → "purchase completed"
|
|
215
|
-
* • Breakdown: user property "segment"
|
|
216
|
-
* • Expected: frequent_buyer ≈ 39% vs window_shopper ≈ 20%
|
|
217
|
-
*
|
|
218
|
-
* REAL-WORLD ANALOGUE: Returning buyers have established trust
|
|
219
|
-
* and familiarity with the platform, converting at higher rates.
|
|
220
|
-
*
|
|
221
|
-
* ───────────────────────────────────────────────────────────────
|
|
222
|
-
* 9. BROWSE TO PURCHASE TIME-TO-CONVERT (funnel-post)
|
|
223
|
-
* ───────────────────────────────────────────────────────────────
|
|
224
|
-
*
|
|
225
|
-
* PATTERN: Power_seller and frequent_buyer users complete the
|
|
226
|
-
* Browse to Purchase funnel 1.4x faster (factor 0.71); window_shopper
|
|
227
|
-
* 1.4x slower (factor 1.4).
|
|
228
|
-
*
|
|
229
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
230
|
-
*
|
|
231
|
-
* Report 1: Browse to Purchase Median Time-to-Convert by Segment
|
|
232
|
-
* - Funnels > "item searched" -> "item viewed" -> "add to cart" -> "purchase completed"
|
|
233
|
-
* - Measure: Median time to convert
|
|
234
|
-
* - Breakdown: segment
|
|
235
|
-
* - Expected: power/frequent ~ 0.71x; window ~ 1.4x
|
|
236
|
-
*
|
|
237
|
-
* NOTE (funnel-post measurement): visible only via Mixpanel funnel
|
|
238
|
-
* median TTC. Cross-event MIN→MIN SQL queries on raw events do NOT
|
|
239
|
-
* show this — funnel-post adjusts gaps within funnel instances, not
|
|
240
|
-
* across the user's full event history.
|
|
241
|
-
*
|
|
242
|
-
* ───────────────────────────────────────────────────────────────
|
|
243
|
-
* 10. MESSAGE-COUNT MAGIC NUMBER (in-funnel, everything)
|
|
244
|
-
* ───────────────────────────────────────────────────────────────
|
|
245
|
-
*
|
|
246
|
-
* PATTERN: Sweet 2-5 message-sent events between item-viewed and
|
|
247
|
-
* offer-received → +35% on offer_amount of offer-received events.
|
|
248
|
-
* Over 6+ → drop 25% of purchase-completed events (haggling deadlock).
|
|
249
|
-
* No flag.
|
|
250
|
-
*
|
|
251
|
-
* HOW TO FIND IT IN MIXPANEL:
|
|
252
|
-
*
|
|
253
|
-
* Report 1: Avg Offer Amount by Message Bucket
|
|
254
|
-
* - Cohort A: users with 2-5 messages between item-viewed and offer-received
|
|
255
|
-
* - Cohort B: users with 0-1
|
|
256
|
-
* - Event: "offer received"
|
|
257
|
-
* - Measure: Average of "offer_amount"
|
|
258
|
-
* - Expected: A ~ 1.35x B
|
|
259
|
-
*
|
|
260
|
-
* Report 2: Purchase Rate on Heavy Messengers
|
|
261
|
-
* - Cohort C: users with >= 6 messages
|
|
262
|
-
* - Cohort A: users with 2-5
|
|
263
|
-
* - Event: "purchase completed"
|
|
264
|
-
* - Measure: Total per user
|
|
265
|
-
* - Expected: C ~ 25% fewer purchases per user
|
|
266
|
-
*
|
|
267
|
-
* REAL-WORLD ANALOGUE: A few quick clarifying messages close deals;
|
|
268
|
-
* extended haggling kills conversion.
|
|
269
|
-
*
|
|
270
|
-
* ═══════════════════════════════════════════════════════════════
|
|
271
|
-
* EXPECTED METRICS SUMMARY
|
|
272
|
-
* ═══════════════════════════════════════════════════════════════
|
|
273
|
-
*
|
|
274
|
-
* Hook | Metric | Baseline | Effect | Ratio
|
|
275
|
-
* ----------------------------|---------------------|----------|-----------|------
|
|
276
|
-
* Fee Change Impact | listing_fee post-D45| 1x | 1.3x | 1.3x
|
|
277
|
-
* Weekend Shopping Surge | total_amount Sat/Sun| 1x | 1.2x | 1.2x
|
|
278
|
-
* Seller Success → Trust | purchases/user | 1x | 2x | 2x
|
|
279
|
-
* Electronics Category Lift | electronics purch. | 1x | 1.5x | 1.5x
|
|
280
|
-
* Response Time → Conversion | offer_accepted/user | 1x | 2x | 2x
|
|
281
|
-
* New Seller Churn | events post-D14 | 1x | 0.6x | -40%
|
|
282
|
-
* Power Seller Profiles | total_transactions | 0 | 100-500 | --
|
|
283
|
-
* Frequent Buyer Funnel | funnel conversion | 30% | 39% | 1.3x
|
|
284
|
-
* Browse to Purchase T2C | median min by segment| 1x | 0.71/1.4x | ~ 2x range
|
|
285
|
-
* Message Magic Number | sweet offer_amount | 1x | 1.35x | 1.35x
|
|
286
|
-
* Message Magic Number | over purchases/user | 1x | 0.75x | -25%
|
|
287
|
-
*/
|
|
288
|
-
|
|
289
|
-
// ── SCALE ──
|
|
290
|
-
const SEED = "dm4-marketplace";
|
|
291
|
-
const NUM_USERS = 10_000;
|
|
292
|
-
const DATASET_START = "2026-01-01T00:00:00Z";
|
|
293
|
-
const DATASET_END = "2026-05-01T23:59:59Z";
|
|
294
|
-
const EVENTS_PER_DAY = 1.2;
|
|
295
|
-
const token = process.env.MP_TOKEN || "your-mixpanel-token";
|
|
296
|
-
|
|
297
|
-
const chance = u.initChance(SEED);
|
|
298
|
-
|
|
299
|
-
// ── KNOBS (tweak these to reshape stories) ──
|
|
300
|
-
const FEE_CHANGE_DAY = 45;
|
|
301
|
-
const FEE_CHANGE_MULT = 1.3;
|
|
302
|
-
|
|
303
|
-
const WEEKEND_SPEND_MULT = 1.2;
|
|
304
|
-
|
|
305
|
-
const POWER_SELLER_CLONE_LIKELIHOOD = 65;
|
|
306
|
-
|
|
307
|
-
const ELECTRONICS_CLONE_LIKELIHOOD = 40;
|
|
308
|
-
const ELECTRONICS_MAX_CLONES = 3;
|
|
309
|
-
|
|
310
|
-
const FAST_RESPONDER_HASH_MOD = 5;
|
|
311
|
-
const FAST_RESPONDER_HASH_THRESHOLD = 2;
|
|
312
|
-
const FAST_OFFER_CLONE_COUNT = 2;
|
|
313
|
-
const FAST_OFFER_FROM_RECEIVED_LIKELIHOOD = 60;
|
|
314
|
-
const SLOW_OFFER_DROP_LIKELIHOOD = 60;
|
|
315
|
-
|
|
316
|
-
const NEW_SELLER_CUTOFF_DAYS = 14;
|
|
317
|
-
const NEW_SELLER_DROP_LIKELIHOOD = 50;
|
|
318
|
-
|
|
319
|
-
const FREQUENT_BUYER_DROP_LIKELIHOOD = 25;
|
|
320
|
-
|
|
321
|
-
const TTC_FAST_FACTOR = 0.71;
|
|
322
|
-
const TTC_SLOW_FACTOR = 1.4;
|
|
323
|
-
|
|
324
|
-
const MSG_SWEET_MIN = 2;
|
|
325
|
-
const MSG_SWEET_MAX = 5;
|
|
326
|
-
const MSG_OVER_THRESHOLD = 6;
|
|
327
|
-
const MSG_OFFER_BOOST = 1.35;
|
|
328
|
-
const MSG_PURCHASE_DROP_LIKELIHOOD = 25;
|
|
329
|
-
|
|
330
|
-
// ── DATA ARRAYS ──
|
|
331
|
-
// Generate consistent seller store and listing IDs at module level
|
|
332
|
-
const storeIds = v.range(1, 150).map(() => `STORE_${v.uid(6)}`);
|
|
333
|
-
const listingIds = v.range(1, 500).map(() => `LST_${v.uid(8)}`);
|
|
334
|
-
|
|
335
|
-
// ── HELPER FUNCTIONS ──
|
|
336
|
-
function handleUserHooks(record) {
|
|
337
|
-
// H7: Power seller profiles — enrich segment-tagged profiles
|
|
338
|
-
if (record.segment === "power_seller") {
|
|
339
|
-
record.total_transactions = chance.integer({ min: 100, max: 500 });
|
|
340
|
-
record.seller_rating = chance.floating({ min: 4.5, max: 5.0, fixed: 1 });
|
|
341
|
-
record.store_name = chance.pickone([
|
|
342
|
-
"TechVault Pro", "StyleHaven", "GearFactory", "PrimeFinds",
|
|
343
|
-
"TopShelf Goods", "EliteDeals", "QualityFirst Store", "BestBuy Resale",
|
|
344
|
-
]);
|
|
345
|
-
} else if (record.segment === "casual_seller") {
|
|
346
|
-
record.total_transactions = chance.integer({ min: 5, max: 50 });
|
|
347
|
-
record.seller_rating = chance.floating({ min: 3.0, max: 4.5, fixed: 1 });
|
|
348
|
-
record.store_name = chance.pickone([
|
|
349
|
-
"My Garage Sale", "Closet Cleanout", "Random Finds", "Weekend Seller",
|
|
350
|
-
]);
|
|
351
|
-
} else if (record.segment === "new_seller") {
|
|
352
|
-
record.total_transactions = chance.integer({ min: 0, max: 3 });
|
|
353
|
-
record.seller_rating = 0;
|
|
354
|
-
record.store_name = "New Store";
|
|
355
|
-
}
|
|
356
|
-
return record;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function handleFunnelPostHooks(record, meta) {
|
|
360
|
-
// H9: Browse-to-purchase TTC scaled by segment
|
|
361
|
-
const segment = meta?.profile?.segment;
|
|
362
|
-
if (Array.isArray(record) && record.length > 1) {
|
|
363
|
-
const factor = (
|
|
364
|
-
segment === "power_seller" || segment === "frequent_buyer" ? TTC_FAST_FACTOR :
|
|
365
|
-
segment === "window_shopper" ? TTC_SLOW_FACTOR :
|
|
366
|
-
1.0
|
|
367
|
-
);
|
|
368
|
-
if (factor !== 1.0) {
|
|
369
|
-
for (let i = 1; i < record.length; i++) {
|
|
370
|
-
const prev = dayjs(record[i - 1].time);
|
|
371
|
-
const newGap = Math.round(dayjs(record[i].time).diff(prev) * factor);
|
|
372
|
-
record[i].time = prev.add(newGap, "milliseconds").toISOString();
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return record;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function handleEverythingHooks(record, meta) {
|
|
380
|
-
const datasetStart = dayjs.unix(meta.datasetStart);
|
|
381
|
-
const FEE_CHANGE_CUTOFF = datasetStart.add(FEE_CHANGE_DAY, "days");
|
|
382
|
-
let events = record;
|
|
383
|
-
if (!events.length) return record;
|
|
384
|
-
|
|
385
|
-
const profile = meta.profile;
|
|
386
|
-
|
|
387
|
-
// Stamp superProps from profile so they are consistent per user.
|
|
388
|
-
if (profile) {
|
|
389
|
-
events.forEach(e => {
|
|
390
|
-
if (profile.Platform) e.Platform = profile.Platform;
|
|
391
|
-
if (profile.category) e.category = profile.category;
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// H1: Fee change impact — listings after d45 get listing_fee 1.3x.
|
|
396
|
-
// In everything hook so timestamp comparison sees post-bunchIntoSessions times.
|
|
397
|
-
events.forEach(e => {
|
|
398
|
-
if (e.event === "listing created" && dayjs(e.time).isAfter(FEE_CHANGE_CUTOFF)) {
|
|
399
|
-
e.listing_fee = Math.floor((e.listing_fee || 15) * FEE_CHANGE_MULT);
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
// H8: Frequent buyer conversion filter — non-frequent-buyer users
|
|
404
|
-
// drop ~25% of "purchase completed" events.
|
|
405
|
-
if (profile && profile.segment !== "frequent_buyer") {
|
|
406
|
-
record = record.filter(e => {
|
|
407
|
-
if (e.event === "purchase completed" && chance.bool({ likelihood: FREQUENT_BUYER_DROP_LIKELIHOOD })) return false;
|
|
408
|
-
return true;
|
|
409
|
-
});
|
|
410
|
-
events = record;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// H3: Seller success → buyer trust — power sellers get 2x purchase events cloned.
|
|
414
|
-
if (profile && profile.segment === "power_seller") {
|
|
415
|
-
const purchases = events.filter(e => e.event === "purchase completed");
|
|
416
|
-
const templatePurchase = purchases[0];
|
|
417
|
-
if (templatePurchase && purchases.length > 0) {
|
|
418
|
-
purchases.forEach(p => {
|
|
419
|
-
if (chance.bool({ likelihood: POWER_SELLER_CLONE_LIKELIHOOD })) {
|
|
420
|
-
events.push({
|
|
421
|
-
...templatePurchase,
|
|
422
|
-
time: dayjs(p.time).add(chance.integer({ min: 1, max: 48 }), "hours").toISOString(),
|
|
423
|
-
user_id: p.user_id,
|
|
424
|
-
total_amount: chance.integer({ min: 15, max: 400 }),
|
|
425
|
-
item_count: chance.integer({ min: 1, max: 4 }),
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// H4: Search-to-purchase by category — electronics gets cloned purchase events.
|
|
433
|
-
const hasElectronicsSearch = events.some(e =>
|
|
434
|
-
e.event === "item searched" && e.category === "electronics"
|
|
435
|
-
);
|
|
436
|
-
if (hasElectronicsSearch) {
|
|
437
|
-
const templatePurchase = events.find(e => e.event === "purchase completed");
|
|
438
|
-
if (templatePurchase) {
|
|
439
|
-
const electronicsSearches = events.filter(e =>
|
|
440
|
-
e.event === "item searched" && e.category === "electronics"
|
|
441
|
-
);
|
|
442
|
-
electronicsSearches.slice(0, ELECTRONICS_MAX_CLONES).forEach(search => {
|
|
443
|
-
if (chance.bool({ likelihood: ELECTRONICS_CLONE_LIKELIHOOD })) {
|
|
444
|
-
events.push({
|
|
445
|
-
...templatePurchase,
|
|
446
|
-
time: dayjs(search.time).add(chance.integer({ min: 1, max: 12 }), "hours").toISOString(),
|
|
447
|
-
user_id: search.user_id,
|
|
448
|
-
total_amount: chance.integer({ min: 50, max: 300 }),
|
|
449
|
-
category: "electronics",
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// H5: Response time → conversion — deterministic fast/slow cohorts via user hash.
|
|
457
|
-
// Fast (~40%): set response_time_hours 1-4, clone offer_accepted.
|
|
458
|
-
// Slow (~60%): set 8-36, drop 60% of offer_accepted events.
|
|
459
|
-
const msgEvents = events.filter(e => e.event === "message sent");
|
|
460
|
-
if (msgEvents.length > 0) {
|
|
461
|
-
const uid = msgEvents[0].user_id || "";
|
|
462
|
-
const isFast = (uid.charCodeAt(0) + uid.charCodeAt(uid.length - 1)) % FAST_RESPONDER_HASH_MOD < FAST_RESPONDER_HASH_THRESHOLD;
|
|
463
|
-
msgEvents.forEach(m => {
|
|
464
|
-
m.response_time_hours = isFast
|
|
465
|
-
? chance.floating({ min: 0.5, max: 4, fixed: 1 })
|
|
466
|
-
: chance.floating({ min: 8, max: 36, fixed: 1 });
|
|
467
|
-
});
|
|
468
|
-
const templateOffer = events.find(e => e.event === "offer accepted");
|
|
469
|
-
if (isFast && templateOffer) {
|
|
470
|
-
// Fast responders: clone existing offer_accepted 2x each
|
|
471
|
-
const existingAccepts = events.filter(e => e.event === "offer accepted");
|
|
472
|
-
existingAccepts.forEach(accept => {
|
|
473
|
-
for (let c = 0; c < FAST_OFFER_CLONE_COUNT; c++) {
|
|
474
|
-
events.push({
|
|
475
|
-
...accept,
|
|
476
|
-
time: dayjs(accept.time).add(chance.integer({ min: 1, max: 8 }), "hours").toISOString(),
|
|
477
|
-
user_id: accept.user_id,
|
|
478
|
-
response_time_hours: chance.floating({ min: 0.1, max: 2, fixed: 1 }),
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
// Also clone from offer_received → offer_accepted
|
|
483
|
-
const offers = events.filter(e => e.event === "offer received");
|
|
484
|
-
offers.forEach(offer => {
|
|
485
|
-
if (chance.bool({ likelihood: FAST_OFFER_FROM_RECEIVED_LIKELIHOOD })) {
|
|
486
|
-
events.push({
|
|
487
|
-
...templateOffer,
|
|
488
|
-
time: dayjs(offer.time).add(chance.integer({ min: 1, max: 6 }), "hours").toISOString(),
|
|
489
|
-
user_id: offer.user_id,
|
|
490
|
-
response_time_hours: chance.floating({ min: 0.1, max: 3, fixed: 1 }),
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
} else if (!isFast) {
|
|
495
|
-
// Slow responders: drop 60% of offer_accepted events
|
|
496
|
-
for (let i = events.length - 1; i >= 0; i--) {
|
|
497
|
-
if (events[i].event === "offer accepted" && chance.bool({ likelihood: SLOW_OFFER_DROP_LIKELIHOOD })) {
|
|
498
|
-
events.splice(i, 1);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// H6: New seller churn — drop 50% of events after user's first 14 days.
|
|
505
|
-
if (profile && profile.segment === "new_seller") {
|
|
506
|
-
const firstEventTime = events.length > 0 ? dayjs(events[0].time) : null;
|
|
507
|
-
if (firstEventTime) {
|
|
508
|
-
const userCutoff = firstEventTime.add(NEW_SELLER_CUTOFF_DAYS, "days");
|
|
509
|
-
for (let i = events.length - 1; i >= 0; i--) {
|
|
510
|
-
if (dayjs(events[i].time).isAfter(userCutoff) && chance.bool({ likelihood: NEW_SELLER_DROP_LIKELIHOOD })) {
|
|
511
|
-
events.splice(i, 1);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// H2: Weekend shopping surge — Sat/Sun purchases get total_amount 1.2x.
|
|
518
|
-
events.forEach(e => {
|
|
519
|
-
if (e.event === "purchase completed") {
|
|
520
|
-
const dow = new Date(e.time).getUTCDay();
|
|
521
|
-
if (dow === 0 || dow === 6) {
|
|
522
|
-
e.total_amount = Math.floor((e.total_amount || 60) * WEEKEND_SPEND_MULT);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
// H10: Message-count magic number (in-funnel, no flags)
|
|
528
|
-
// Sweet 2-5 messages between item-viewed and offer-received →
|
|
529
|
-
// +35% on offer-received offer_amount. Over 6+ → drop 25% of purchases.
|
|
530
|
-
const itemViewed = events.find(e => e.event === "item viewed");
|
|
531
|
-
const offerReceived = events.find(e => e.event === "offer received");
|
|
532
|
-
if (itemViewed && offerReceived) {
|
|
533
|
-
const aTime = dayjs(itemViewed.time);
|
|
534
|
-
const bTime = dayjs(offerReceived.time);
|
|
535
|
-
const msgBetween = events.filter(e =>
|
|
536
|
-
e.event === "message sent" &&
|
|
537
|
-
dayjs(e.time).isAfter(aTime) &&
|
|
538
|
-
dayjs(e.time).isBefore(bTime)
|
|
539
|
-
).length;
|
|
540
|
-
if (msgBetween >= MSG_SWEET_MIN && msgBetween <= MSG_SWEET_MAX) {
|
|
541
|
-
events.forEach(e => {
|
|
542
|
-
if (e.event === "offer received" && typeof e.offer_amount === "number") {
|
|
543
|
-
e.offer_amount = Math.round(e.offer_amount * MSG_OFFER_BOOST);
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
} else if (msgBetween >= MSG_OVER_THRESHOLD) {
|
|
547
|
-
for (let i = events.length - 1; i >= 0; i--) {
|
|
548
|
-
if (events[i].event === "purchase completed" && chance.bool({ likelihood: MSG_PURCHASE_DROP_LIKELIHOOD })) {
|
|
549
|
-
events.splice(i, 1);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
return record;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// ── CONFIG ──
|
|
559
|
-
/** @type {Config} */
|
|
560
|
-
const config = {
|
|
561
|
-
version: 2,
|
|
562
|
-
seed: SEED,
|
|
563
|
-
datasetStart: DATASET_START,
|
|
564
|
-
datasetEnd: DATASET_END,
|
|
565
|
-
avgEventsPerUserPerDay: EVENTS_PER_DAY,
|
|
566
|
-
numUsers: NUM_USERS,
|
|
567
|
-
format: "json",
|
|
568
|
-
gzip: true,
|
|
569
|
-
credentials: {
|
|
570
|
-
token,
|
|
571
|
-
},
|
|
572
|
-
switches: {
|
|
573
|
-
hasSessionIds: true,
|
|
574
|
-
alsoInferFunnels: false,
|
|
575
|
-
hasLocation: true,
|
|
576
|
-
hasAndroidDevices: true,
|
|
577
|
-
hasIOSDevices: true,
|
|
578
|
-
hasDesktopDevices: true,
|
|
579
|
-
hasBrowser: false,
|
|
580
|
-
hasCampaigns: false,
|
|
581
|
-
isAnonymous: false,
|
|
582
|
-
hasAdSpend: false,
|
|
583
|
-
hasAvatar: true,
|
|
584
|
-
},
|
|
585
|
-
identity: {
|
|
586
|
-
avgDevicePerUser: 2,
|
|
587
|
-
},
|
|
588
|
-
concurrency: 1,
|
|
589
|
-
writeToDisk: false,
|
|
590
|
-
scdProps: {
|
|
591
|
-
seller_tier: {
|
|
592
|
-
values: ["new", "verified", "power", "featured"],
|
|
593
|
-
frequency: "month",
|
|
594
|
-
timing: "fuzzy",
|
|
595
|
-
max: 8
|
|
596
|
-
}
|
|
597
|
-
},
|
|
598
|
-
mirrorProps: {},
|
|
599
|
-
lookupTables: [],
|
|
600
|
-
|
|
601
|
-
events: [
|
|
602
|
-
{
|
|
603
|
-
event: "account created",
|
|
604
|
-
weight: 1,
|
|
605
|
-
isFirstEvent: true,
|
|
606
|
-
isAuthEvent: true,
|
|
607
|
-
properties: {
|
|
608
|
-
signup_source: ["organic", "google_shopping", "tiktok", "seller_referral", "email_campaign", "word_of_mouth"],
|
|
609
|
-
},
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
event: "item searched",
|
|
613
|
-
weight: 8,
|
|
614
|
-
isStrictEvent: false,
|
|
615
|
-
properties: {
|
|
616
|
-
search_query: ["laptop", "vintage jacket", "sneakers", "headphones", "phone case", "gaming console", "watch", "sunglasses", "backpack", "camera"],
|
|
617
|
-
results_count: u.weighNumRange(0, 50, 0.5),
|
|
618
|
-
sort_by: ["relevance", "relevance", "price_low", "price_high", "newest", "best_selling"],
|
|
619
|
-
},
|
|
620
|
-
},
|
|
621
|
-
{
|
|
622
|
-
event: "item viewed",
|
|
623
|
-
weight: 7,
|
|
624
|
-
isStrictEvent: false,
|
|
625
|
-
properties: {
|
|
626
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
627
|
-
item_price: u.weighNumRange(5, 500, 0.3, 45),
|
|
628
|
-
condition: ["new", "new", "like_new", "good", "fair"],
|
|
629
|
-
seller_rating: u.weighNumRange(1, 5, 0.7, 4),
|
|
630
|
-
photos_count: u.weighNumRange(1, 10, 0.5, 3),
|
|
631
|
-
},
|
|
632
|
-
},
|
|
633
|
-
{
|
|
634
|
-
event: "add to cart",
|
|
635
|
-
weight: 5,
|
|
636
|
-
properties: {
|
|
637
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
638
|
-
item_price: u.weighNumRange(5, 500, 0.3, 45),
|
|
639
|
-
quantity: u.weighNumRange(1, 5, 0.2),
|
|
640
|
-
},
|
|
641
|
-
},
|
|
642
|
-
{
|
|
643
|
-
event: "purchase completed",
|
|
644
|
-
weight: 3,
|
|
645
|
-
isStrictEvent: false,
|
|
646
|
-
properties: {
|
|
647
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
648
|
-
total_amount: u.weighNumRange(10, 500, 0.3, 60),
|
|
649
|
-
item_count: u.weighNumRange(1, 5, 0.3),
|
|
650
|
-
payment_method: ["credit_card", "credit_card", "paypal", "apple_pay", "debit"],
|
|
651
|
-
shipping_method: ["standard", "standard", "express", "pickup"],
|
|
652
|
-
},
|
|
653
|
-
},
|
|
654
|
-
{
|
|
655
|
-
event: "listing created",
|
|
656
|
-
weight: 4,
|
|
657
|
-
isStrictEvent: false,
|
|
658
|
-
properties: {
|
|
659
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
660
|
-
store_id: chance.pickone.bind(chance, storeIds),
|
|
661
|
-
asking_price: u.weighNumRange(5, 500, 0.3, 50),
|
|
662
|
-
condition: ["new", "new", "like_new", "good", "fair"],
|
|
663
|
-
listing_fee: u.weighNumRange(5, 30, 0.5, 15),
|
|
664
|
-
},
|
|
665
|
-
},
|
|
666
|
-
{
|
|
667
|
-
event: "listing updated",
|
|
668
|
-
weight: 3,
|
|
669
|
-
properties: {
|
|
670
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
671
|
-
field_updated: ["price", "price", "description", "photos", "condition", "shipping"],
|
|
672
|
-
price_change_pct: u.weighNumRange(-30, 30, 0.5),
|
|
673
|
-
},
|
|
674
|
-
},
|
|
675
|
-
{
|
|
676
|
-
event: "offer received",
|
|
677
|
-
weight: 3,
|
|
678
|
-
isStrictEvent: false,
|
|
679
|
-
properties: {
|
|
680
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
681
|
-
offer_amount: u.weighNumRange(5, 400, 0.3, 40),
|
|
682
|
-
offer_pct_of_asking: u.weighNumRange(50, 100, 0.5, 80),
|
|
683
|
-
},
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
event: "offer accepted",
|
|
687
|
-
weight: 2,
|
|
688
|
-
isStrictEvent: false,
|
|
689
|
-
properties: {
|
|
690
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
691
|
-
final_price: u.weighNumRange(10, 500, 0.3, 55),
|
|
692
|
-
negotiation_rounds: u.weighNumRange(1, 5, 0.3),
|
|
693
|
-
response_time_hours: u.weighNumRange(0.1, 48, 0.3, 4),
|
|
694
|
-
},
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
event: "review submitted",
|
|
698
|
-
weight: 2,
|
|
699
|
-
properties: {
|
|
700
|
-
listing_id: chance.pickone.bind(chance, listingIds),
|
|
701
|
-
rating: u.weighNumRange(1, 5, 0.7, 4),
|
|
702
|
-
review_length: u.weighNumRange(10, 300, 0.5, 80),
|
|
703
|
-
is_verified_purchase: [true, true, true, false],
|
|
704
|
-
},
|
|
705
|
-
},
|
|
706
|
-
{
|
|
707
|
-
event: "seller rated",
|
|
708
|
-
weight: 2,
|
|
709
|
-
properties: {
|
|
710
|
-
store_id: chance.pickone.bind(chance, storeIds),
|
|
711
|
-
seller_rating: u.weighNumRange(1, 5, 0.7, 4),
|
|
712
|
-
would_buy_again: [true, true, true, true, false],
|
|
713
|
-
},
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
event: "message sent",
|
|
717
|
-
weight: 5,
|
|
718
|
-
isStrictEvent: false,
|
|
719
|
-
properties: {
|
|
720
|
-
message_type: ["inquiry", "inquiry", "negotiation", "shipping_question", "complaint", "thank_you"],
|
|
721
|
-
recipient_type: ["seller", "seller", "buyer", "support"],
|
|
722
|
-
response_time_hours: u.weighNumRange(0.1, 48, 0.3, 6),
|
|
723
|
-
},
|
|
724
|
-
},
|
|
725
|
-
{
|
|
726
|
-
event: "shipping updated",
|
|
727
|
-
weight: 3,
|
|
728
|
-
properties: {
|
|
729
|
-
status: ["label_created", "in_transit", "in_transit", "out_for_delivery", "delivered", "delivered"],
|
|
730
|
-
carrier: ["usps", "ups", "fedex", "dhl"],
|
|
731
|
-
tracking_viewed: [true, true, false],
|
|
732
|
-
},
|
|
733
|
-
},
|
|
734
|
-
{
|
|
735
|
-
event: "refund requested",
|
|
736
|
-
weight: 1,
|
|
737
|
-
properties: {
|
|
738
|
-
reason: ["not_as_described", "damaged", "wrong_item", "changed_mind", "late_delivery"],
|
|
739
|
-
refund_amount: u.weighNumRange(5, 300, 0.4, 40),
|
|
740
|
-
resolution_status: ["pending", "approved", "approved", "denied"],
|
|
741
|
-
},
|
|
742
|
-
},
|
|
743
|
-
{
|
|
744
|
-
event: "notification received",
|
|
745
|
-
weight: 6,
|
|
746
|
-
properties: {
|
|
747
|
-
notification_type: ["price_drop", "price_drop", "new_offer", "shipping_update", "message_received", "sale_reminder"],
|
|
748
|
-
channel: ["push", "push", "email", "sms"],
|
|
749
|
-
opened: [true, true, true, false],
|
|
750
|
-
},
|
|
751
|
-
},
|
|
752
|
-
{
|
|
753
|
-
event: "profile updated",
|
|
754
|
-
weight: 2,
|
|
755
|
-
properties: {
|
|
756
|
-
field_updated: ["address", "payment_method", "profile_photo", "bio", "phone", "store_description"],
|
|
757
|
-
},
|
|
758
|
-
},
|
|
759
|
-
{
|
|
760
|
-
event: "app session",
|
|
761
|
-
weight: 8,
|
|
762
|
-
properties: {
|
|
763
|
-
session_duration_sec: u.weighNumRange(10, 1800, 0.4, 120),
|
|
764
|
-
pages_viewed: u.weighNumRange(1, 20, 0.5, 4),
|
|
765
|
-
},
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
event: "account deactivated",
|
|
769
|
-
weight: 1,
|
|
770
|
-
isChurnEvent: true,
|
|
771
|
-
returnLikelihood: 0.15,
|
|
772
|
-
isStrictEvent: true,
|
|
773
|
-
properties: {
|
|
774
|
-
reason: ["low_sales", "high_fees", "found_alternative", "bad_experience", "no_longer_needed"],
|
|
775
|
-
},
|
|
776
|
-
},
|
|
777
|
-
],
|
|
778
|
-
|
|
779
|
-
funnels: [
|
|
780
|
-
{
|
|
781
|
-
name: "Buyer Onboarding",
|
|
782
|
-
sequence: ["account created", "item searched", "item viewed", "add to cart"],
|
|
783
|
-
conversionRate: 40,
|
|
784
|
-
order: "sequential",
|
|
785
|
-
isFirstFunnel: true,
|
|
786
|
-
timeToConvert: 72,
|
|
787
|
-
weight: 3,
|
|
788
|
-
},
|
|
789
|
-
{
|
|
790
|
-
name: "Browse to Purchase",
|
|
791
|
-
sequence: ["item searched", "item viewed", "add to cart", "purchase completed"],
|
|
792
|
-
conversionRate: 30,
|
|
793
|
-
order: "sequential",
|
|
794
|
-
timeToConvert: 48,
|
|
795
|
-
weight: 5,
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
name: "Seller Listing Flow",
|
|
799
|
-
sequence: ["listing created", "listing updated", "offer received", "offer accepted"],
|
|
800
|
-
conversionRate: 25,
|
|
801
|
-
order: "sequential",
|
|
802
|
-
timeToConvert: 168,
|
|
803
|
-
weight: 3,
|
|
804
|
-
},
|
|
805
|
-
{
|
|
806
|
-
name: "Offer Negotiation",
|
|
807
|
-
sequence: ["offer received", "message sent", "offer accepted"],
|
|
808
|
-
conversionRate: 35,
|
|
809
|
-
order: "sequential",
|
|
810
|
-
timeToConvert: 72,
|
|
811
|
-
weight: 2,
|
|
812
|
-
},
|
|
813
|
-
{
|
|
814
|
-
name: "Review After Purchase",
|
|
815
|
-
sequence: ["purchase completed", "shipping updated", "review submitted"],
|
|
816
|
-
conversionRate: 20,
|
|
817
|
-
order: "sequential",
|
|
818
|
-
timeToConvert: 336,
|
|
819
|
-
weight: 2,
|
|
820
|
-
},
|
|
821
|
-
],
|
|
822
|
-
|
|
823
|
-
superProps: {
|
|
824
|
-
Platform: ["ios", "android", "web"],
|
|
825
|
-
category: ["electronics", "clothing", "home_garden", "collectibles", "sports", "toys", "automotive"],
|
|
826
|
-
},
|
|
827
|
-
|
|
828
|
-
userProps: {
|
|
829
|
-
user_type: ["buyer"],
|
|
830
|
-
segment: ["window_shopper"],
|
|
831
|
-
seller_rating: u.weighNumRange(0, 5, 0.5),
|
|
832
|
-
total_transactions: [0],
|
|
833
|
-
response_time_hours: u.weighNumRange(0, 48, 0.3),
|
|
834
|
-
store_name: ["none"],
|
|
835
|
-
Platform: ["ios", "android", "web"],
|
|
836
|
-
category: ["electronics", "clothing", "home_garden", "collectibles", "sports", "toys", "automotive"],
|
|
837
|
-
},
|
|
838
|
-
|
|
839
|
-
personas: [
|
|
840
|
-
{
|
|
841
|
-
name: "power_seller",
|
|
842
|
-
weight: 8,
|
|
843
|
-
eventMultiplier: 5.0,
|
|
844
|
-
conversionModifier: 1.8,
|
|
845
|
-
churnRate: 0.01,
|
|
846
|
-
properties: {
|
|
847
|
-
user_type: "seller",
|
|
848
|
-
segment: "power_seller",
|
|
849
|
-
},
|
|
850
|
-
},
|
|
851
|
-
{
|
|
852
|
-
name: "casual_seller",
|
|
853
|
-
weight: 20,
|
|
854
|
-
eventMultiplier: 1.5,
|
|
855
|
-
conversionModifier: 1.0,
|
|
856
|
-
churnRate: 0.08,
|
|
857
|
-
properties: {
|
|
858
|
-
user_type: "seller",
|
|
859
|
-
segment: "casual_seller",
|
|
860
|
-
},
|
|
861
|
-
},
|
|
862
|
-
{
|
|
863
|
-
name: "frequent_buyer",
|
|
864
|
-
weight: 25,
|
|
865
|
-
eventMultiplier: 2.0,
|
|
866
|
-
conversionModifier: 1.3,
|
|
867
|
-
churnRate: 0.03,
|
|
868
|
-
properties: {
|
|
869
|
-
user_type: "buyer",
|
|
870
|
-
segment: "frequent_buyer",
|
|
871
|
-
},
|
|
872
|
-
},
|
|
873
|
-
{
|
|
874
|
-
name: "window_shopper",
|
|
875
|
-
weight: 35,
|
|
876
|
-
eventMultiplier: 0.5,
|
|
877
|
-
conversionModifier: 0.4,
|
|
878
|
-
churnRate: 0.15,
|
|
879
|
-
properties: {
|
|
880
|
-
user_type: "buyer",
|
|
881
|
-
segment: "window_shopper",
|
|
882
|
-
},
|
|
883
|
-
},
|
|
884
|
-
{
|
|
885
|
-
name: "new_seller",
|
|
886
|
-
weight: 12,
|
|
887
|
-
eventMultiplier: 0.8,
|
|
888
|
-
conversionModifier: 0.5,
|
|
889
|
-
churnRate: 0.3,
|
|
890
|
-
properties: {
|
|
891
|
-
user_type: "seller",
|
|
892
|
-
segment: "new_seller",
|
|
893
|
-
},
|
|
894
|
-
activeWindow: { maxDays: 28 },
|
|
895
|
-
},
|
|
896
|
-
],
|
|
897
|
-
|
|
898
|
-
worldEvents: [
|
|
899
|
-
{
|
|
900
|
-
name: "marketplace_fee_change",
|
|
901
|
-
type: "product_launch",
|
|
902
|
-
startDay: 45,
|
|
903
|
-
duration: null,
|
|
904
|
-
injectProps: { fee_change: "increased" },
|
|
905
|
-
affectsEvents: ["listing created"],
|
|
906
|
-
},
|
|
907
|
-
{
|
|
908
|
-
name: "viral_tiktok_moment",
|
|
909
|
-
type: "campaign",
|
|
910
|
-
startDay: 55,
|
|
911
|
-
duration: 3,
|
|
912
|
-
volumeMultiplier: 3.0,
|
|
913
|
-
affectsEvents: ["item searched", "item viewed", "purchase completed"],
|
|
914
|
-
},
|
|
915
|
-
],
|
|
916
|
-
|
|
917
|
-
engagementDecay: {
|
|
918
|
-
model: "exponential",
|
|
919
|
-
halfLife: 75,
|
|
920
|
-
floor: 0.1,
|
|
921
|
-
},
|
|
922
|
-
|
|
923
|
-
attribution: {
|
|
924
|
-
model: "last_touch",
|
|
925
|
-
window: 7,
|
|
926
|
-
campaigns: [
|
|
927
|
-
{
|
|
928
|
-
name: "google_shopping",
|
|
929
|
-
source: "google",
|
|
930
|
-
medium: "cpc",
|
|
931
|
-
activeDays: [0, 100],
|
|
932
|
-
dailyBudget: [200, 800],
|
|
933
|
-
acquisitionRate: 0.03,
|
|
934
|
-
userPersonaBias: { frequent_buyer: 0.5 },
|
|
935
|
-
},
|
|
936
|
-
{
|
|
937
|
-
name: "tiktok",
|
|
938
|
-
source: "tiktok",
|
|
939
|
-
medium: "social",
|
|
940
|
-
activeDays: [10, 90],
|
|
941
|
-
dailyBudget: [100, 400],
|
|
942
|
-
acquisitionRate: 0.02,
|
|
943
|
-
userPersonaBias: { window_shopper: 0.5 },
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
name: "seller_referral",
|
|
947
|
-
source: "referral",
|
|
948
|
-
medium: "referral",
|
|
949
|
-
activeDays: [0, 100],
|
|
950
|
-
dailyBudget: [50, 200],
|
|
951
|
-
acquisitionRate: 0.02,
|
|
952
|
-
userPersonaBias: { power_seller: 0.4 },
|
|
953
|
-
},
|
|
954
|
-
],
|
|
955
|
-
organicRate: 0.30,
|
|
956
|
-
},
|
|
957
|
-
|
|
958
|
-
geo: {
|
|
959
|
-
sticky: true,
|
|
960
|
-
regions: [
|
|
961
|
-
{
|
|
962
|
-
name: "us",
|
|
963
|
-
countries: ["US"],
|
|
964
|
-
weight: 50,
|
|
965
|
-
timezoneOffset: -5,
|
|
966
|
-
properties: { currency: "USD", locale: "en-US" },
|
|
967
|
-
},
|
|
968
|
-
{
|
|
969
|
-
name: "uk",
|
|
970
|
-
countries: ["GB"],
|
|
971
|
-
weight: 20,
|
|
972
|
-
timezoneOffset: 0,
|
|
973
|
-
properties: { currency: "GBP", locale: "en-GB" },
|
|
974
|
-
},
|
|
975
|
-
{
|
|
976
|
-
name: "australia",
|
|
977
|
-
countries: ["AU"],
|
|
978
|
-
weight: 15,
|
|
979
|
-
timezoneOffset: 10,
|
|
980
|
-
properties: { currency: "AUD", locale: "en-AU" },
|
|
981
|
-
},
|
|
982
|
-
{
|
|
983
|
-
name: "canada",
|
|
984
|
-
countries: ["CA"],
|
|
985
|
-
weight: 15,
|
|
986
|
-
timezoneOffset: -5,
|
|
987
|
-
properties: { currency: "CAD", locale: "en-CA" },
|
|
988
|
-
},
|
|
989
|
-
],
|
|
990
|
-
},
|
|
991
|
-
|
|
992
|
-
anomalies: [
|
|
993
|
-
{
|
|
994
|
-
type: "extreme_value",
|
|
995
|
-
event: "purchase completed",
|
|
996
|
-
property: "total_amount",
|
|
997
|
-
frequency: 0.002,
|
|
998
|
-
multiplier: 30,
|
|
999
|
-
tag: "whale_purchase",
|
|
1000
|
-
},
|
|
1001
|
-
{
|
|
1002
|
-
type: "coordinated",
|
|
1003
|
-
event: "account created",
|
|
1004
|
-
day: 68,
|
|
1005
|
-
window: 0.02,
|
|
1006
|
-
count: 200,
|
|
1007
|
-
tag: "viral_signup",
|
|
1008
|
-
},
|
|
1009
|
-
],
|
|
1010
|
-
|
|
1011
|
-
hook(record, type, meta) {
|
|
1012
|
-
if (type === "user") return handleUserHooks(record);
|
|
1013
|
-
if (type === "funnel-post") return handleFunnelPostHooks(record, meta);
|
|
1014
|
-
if (type === "everything") return handleEverythingHooks(record, meta);
|
|
1015
|
-
return record;
|
|
1016
|
-
},
|
|
1017
|
-
};
|
|
1018
|
-
|
|
1019
|
-
export default config;
|