@ak--47/dungeon-master 1.5.3 → 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.
Files changed (146) hide show
  1. package/.claude/skills/analyze-soup/SKILL.md +21 -12
  2. package/.claude/skills/create-dungeon/SKILL.md +65 -5
  3. package/.claude/skills/create-project/SKILL.md +96 -0
  4. package/.claude/skills/create-project/provision.mjs +336 -0
  5. package/.claude/skills/verify-dungeon/SKILL.md +56 -25
  6. package/.claude/skills/verify-dungeon/references/counting-semantics.md +26 -6
  7. package/.claude/skills/verify-dungeon/references/report-format.md +14 -8
  8. package/.claude/skills/write-hooks/SKILL.md +156 -18
  9. package/CHANGELOG.md +211 -0
  10. package/HOOKS.md +566 -112
  11. package/README.md +28 -9
  12. package/docs/guides/1.3.0-upgrade-guide.md +262 -0
  13. package/docs/guides/1.3.1-upgrade-guide.md +328 -0
  14. package/docs/guides/1.4.0-upgrade-guide.md +130 -0
  15. package/docs/guides/1.4.1-upgrade-guide.md +125 -0
  16. package/docs/guides/1.4.3-upgrade-guide.md +200 -0
  17. package/docs/guides/1.4.4-upgrade-guide.md +95 -0
  18. package/docs/guides/1.5.0-upgrade-guide.md +426 -0
  19. package/docs/guides/1.5.1-upgrade-guide.md +178 -0
  20. package/docs/guides/1.5.3-upgrade-guide.md +117 -0
  21. package/docs/guides/1.6.0-upgrade-guide.md +204 -0
  22. package/dungeons/technical/stories-verify.js +152 -0
  23. package/dungeons/vertical/README.md +154 -0
  24. package/dungeons/vertical/ai-platform/ai-platform.js +1424 -0
  25. package/dungeons/vertical/ai-platform/ai-platform.sql +184 -0
  26. package/dungeons/vertical/ai-platform/ai-platform.verify.mjs +71 -0
  27. package/dungeons/vertical/community/community.js +1589 -0
  28. package/dungeons/vertical/community/community.sql +216 -0
  29. package/dungeons/vertical/community/community.verify.mjs +72 -0
  30. package/dungeons/vertical/crypto/crypto.js +1483 -0
  31. package/dungeons/vertical/crypto/crypto.sql +194 -0
  32. package/dungeons/vertical/crypto/crypto.verify.mjs +78 -0
  33. package/dungeons/vertical/dating/dating.js +1540 -0
  34. package/dungeons/vertical/dating/dating.sql +288 -0
  35. package/dungeons/vertical/dating/dating.verify.mjs +72 -0
  36. package/dungeons/vertical/devtools/devtools.js +1524 -0
  37. package/dungeons/vertical/devtools/devtools.sql +161 -0
  38. package/dungeons/vertical/devtools/devtools.verify.mjs +73 -0
  39. package/dungeons/vertical/ecommerce/ecommerce.js +1480 -0
  40. package/dungeons/vertical/ecommerce/ecommerce.sql +218 -0
  41. package/dungeons/vertical/ecommerce/ecommerce.verify.mjs +70 -0
  42. package/dungeons/vertical/education/education.js +1763 -0
  43. package/dungeons/vertical/education/education.sql +243 -0
  44. package/dungeons/vertical/education/education.verify.mjs +75 -0
  45. package/dungeons/vertical/fintech/fintech.js +1410 -0
  46. package/dungeons/vertical/fintech/fintech.sql +175 -0
  47. package/dungeons/vertical/fintech/fintech.verify.mjs +70 -0
  48. package/dungeons/vertical/fitness/fitness.js +1495 -0
  49. package/dungeons/vertical/fitness/fitness.sql +206 -0
  50. package/dungeons/vertical/fitness/fitness.verify.mjs +71 -0
  51. package/dungeons/vertical/food-delivery/food-delivery.js +1641 -0
  52. package/dungeons/vertical/food-delivery/food-delivery.sql +183 -0
  53. package/dungeons/vertical/food-delivery/food-delivery.verify.mjs +75 -0
  54. package/dungeons/vertical/gaming/gaming.js +1741 -0
  55. package/dungeons/vertical/gaming/gaming.sql +249 -0
  56. package/dungeons/vertical/gaming/gaming.verify.mjs +70 -0
  57. package/dungeons/vertical/healthcare/healthcare.js +1488 -0
  58. package/dungeons/vertical/healthcare/healthcare.sql +193 -0
  59. package/dungeons/vertical/healthcare/healthcare.verify.mjs +72 -0
  60. package/dungeons/vertical/insurance-application/insurance-application.js +1852 -0
  61. package/dungeons/vertical/insurance-application/insurance-application.sql +174 -0
  62. package/dungeons/vertical/insurance-application/insurance-application.verify.mjs +74 -0
  63. package/dungeons/vertical/logistics/logistics.js +1541 -0
  64. package/dungeons/vertical/logistics/logistics.sql +205 -0
  65. package/dungeons/vertical/logistics/logistics.verify.mjs +73 -0
  66. package/dungeons/vertical/marketplace/marketplace.js +1438 -0
  67. package/dungeons/vertical/marketplace/marketplace.sql +148 -0
  68. package/dungeons/vertical/marketplace/marketplace.verify.mjs +74 -0
  69. package/dungeons/vertical/{media.js → media/media.js} +557 -58
  70. package/dungeons/vertical/media/media.sql +173 -0
  71. package/dungeons/vertical/media/media.verify.mjs +74 -0
  72. package/dungeons/vertical/real-estate/real-estate.js +1629 -0
  73. package/dungeons/vertical/real-estate/real-estate.sql +167 -0
  74. package/dungeons/vertical/real-estate/real-estate.verify.mjs +75 -0
  75. package/dungeons/vertical/sass/sass.js +1733 -0
  76. package/dungeons/vertical/sass/sass.sql +173 -0
  77. package/dungeons/vertical/sass/sass.verify.mjs +74 -0
  78. package/dungeons/vertical/social/social.js +1456 -0
  79. package/dungeons/vertical/social/social.sql +160 -0
  80. package/dungeons/vertical/social/social.verify.mjs +76 -0
  81. package/dungeons/vertical/streaming/streaming.js +677 -0
  82. package/dungeons/vertical/streaming/streaming.sql +101 -0
  83. package/dungeons/vertical/streaming/streaming.verify.mjs +75 -0
  84. package/dungeons/vertical/support-desk/support-desk.js +741 -0
  85. package/dungeons/vertical/support-desk/support-desk.sql +137 -0
  86. package/dungeons/vertical/support-desk/support-desk.verify.mjs +75 -0
  87. package/dungeons/vertical/travel/travel.js +1603 -0
  88. package/dungeons/vertical/travel/travel.sql +236 -0
  89. package/dungeons/vertical/travel/travel.verify.mjs +72 -0
  90. package/index.js +2 -2
  91. package/lib/core/config-validator.js +37 -2
  92. package/lib/core/context.js +4 -2
  93. package/lib/core/dungeon-loader.js +20 -15
  94. package/lib/core/dungeon-to-json.js +22 -4
  95. package/lib/core/storage.js +3 -3
  96. package/lib/generators/funnels.js +36 -9
  97. package/lib/hook-helpers/cohort.js +53 -0
  98. package/lib/hook-helpers/index.js +8 -0
  99. package/lib/hook-helpers/mutate.js +1 -1
  100. package/lib/hook-helpers/shape.js +319 -0
  101. package/lib/hook-patterns/aggregate-per-user-by-bin.js +13 -5
  102. package/lib/hook-patterns/attributed-by-source.js +60 -58
  103. package/lib/hook-patterns/frequency-by-frequency.js +14 -4
  104. package/lib/hook-patterns/funnel-frequency-breakdown.js +24 -12
  105. package/lib/hook-patterns/index.js +1 -1
  106. package/lib/hook-patterns/time-to-convert-by-segment.js +75 -13
  107. package/lib/orchestrators/user-loop.js +41 -0
  108. package/lib/templates/story-spec.schema.json +175 -0
  109. package/lib/utils/json-evaluator.js +1 -1
  110. package/lib/utils/utils.js +5 -4
  111. package/lib/verify/coerce.js +186 -0
  112. package/lib/verify/counting.js +270 -32
  113. package/lib/verify/emulate-breakdown.js +1285 -203
  114. package/lib/verify/first-time.js +89 -0
  115. package/lib/verify/flows.js +679 -0
  116. package/lib/verify/formula.js +259 -0
  117. package/lib/verify/funnel-engine.js +697 -113
  118. package/lib/verify/index.js +32 -4
  119. package/lib/verify/sessionize.js +275 -0
  120. package/lib/verify/story-runner.js +483 -0
  121. package/lib/verify/verify-dungeon.js +59 -37
  122. package/package.json +3 -2
  123. package/scripts/extract-dungeon-schema.mjs +2 -2
  124. package/scripts/run-many.mjs +2 -2
  125. package/scripts/verify-runner.mjs +2 -2
  126. package/scripts/verify-stories.mjs +244 -0
  127. package/types.d.ts +296 -30
  128. package/dungeons/vertical/ai-platform.js +0 -818
  129. package/dungeons/vertical/community.js +0 -966
  130. package/dungeons/vertical/crypto.js +0 -895
  131. package/dungeons/vertical/dating.js +0 -830
  132. package/dungeons/vertical/devtools.js +0 -1050
  133. package/dungeons/vertical/ecommerce.js +0 -811
  134. package/dungeons/vertical/education.js +0 -964
  135. package/dungeons/vertical/fintech.js +0 -946
  136. package/dungeons/vertical/fitness.js +0 -935
  137. package/dungeons/vertical/food-delivery.js +0 -934
  138. package/dungeons/vertical/gaming.js +0 -1120
  139. package/dungeons/vertical/healthcare.js +0 -945
  140. package/dungeons/vertical/insurance-application.js +0 -1039
  141. package/dungeons/vertical/logistics.js +0 -986
  142. package/dungeons/vertical/marketplace.js +0 -1019
  143. package/dungeons/vertical/real-estate.js +0 -872
  144. package/dungeons/vertical/sass.js +0 -995
  145. package/dungeons/vertical/social.js +0 -904
  146. package/dungeons/vertical/travel.js +0 -930
@@ -1,1050 +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: CodeForge
13
- * APP: Developer platform for builds, deploys, monitoring, code review,
14
- * and team collaboration. Think GitHub + Vercel + PagerDuty in a
15
- * unified CI/CD experience. Multi-role devs ship code through a
16
- * connect-repo → configure-pipeline → build → deploy → monitor loop.
17
- * SCALE: 10,000 users, ~1.4M events, 121 days (2026-01-01 → 2026-05-01)
18
- * CORE LOOP: connect repo → configure pipeline → build → deploy → monitor
19
- *
20
- * EVENTS (18):
21
- * build completed (8) > app session (8) > pull request created (6)
22
- * > notification received (6) > deployment completed (5)
23
- * > code review completed (5) > monitoring dashboard viewed (5)
24
- * > alert triggered (4) > log searched (4) > incident created (2)
25
- * > incident resolved (2) > repository connected (2) > pipeline configured (2)
26
- * > collaboration invited (2) > environment created (2) > account created (1)
27
- * > billing updated (1) > account deactivated (1)
28
- *
29
- * FUNNELS (5):
30
- * - Onboarding: account created → repository connected → pipeline configured → build completed (45%)
31
- * - Build-Deploy Pipeline: build completed → deployment completed → monitoring dashboard viewed (40%)
32
- * - PR Review Flow: pull request created → code review completed → build completed → deployment completed (35%)
33
- * - Incident Response: alert triggered → incident created → incident resolved (50%)
34
- * - Upgrade Path: app session → billing updated → collaboration invited (20%)
35
- *
36
- * USER PROPS: dev_role, segment, team_size, repos_connected, org_name, experience_level, subscription_tier, Platform, language
37
- * SUPER PROPS: subscription_tier, Platform, language
38
- * SCD PROPS: subscription_tier (free/pro/enterprise, monthly fuzzy, max 6)
39
- * GROUPS: none
40
- */
41
-
42
- // ── HOOK STORIES ──
43
- /*
44
- * NOTE: All cohort effects are HIDDEN — discoverable only via behavioral
45
- * cohorts or raw-prop breakdowns. No cohort flag is stamped on events.
46
- *
47
- * ---------------------------------------------------------------
48
- * 1. BUILD FAILURE CASCADE (event hook)
49
- * ---------------------------------------------------------------
50
- *
51
- * PATTERN: Builds with status "failed" get 2x build_duration_sec.
52
- * Failed builds take longer because the full test suite runs before
53
- * failing, and retries compound the duration.
54
- *
55
- * HOW TO FIND IT IN MIXPANEL:
56
- *
57
- * Report 1: Build Duration by Status
58
- * - Report type: Insights
59
- * - Event: "build completed"
60
- * - Measure: Average of "build_duration_sec"
61
- * - Breakdown: "build_status"
62
- * - Expected: failed ~ 2x longer than success
63
- *
64
- * REAL-WORLD ANALOGUE: Failed CI builds run full test suites,
65
- * timeout, and trigger retry cascades that inflate build times.
66
- *
67
- * ---------------------------------------------------------------
68
- * 2. NIGHT DEPLOY RISK (everything hook)
69
- * ---------------------------------------------------------------
70
- *
71
- * PATTERN: Deployments between 10PM-6AM UTC get deploy_status forced
72
- * to "failed" 40% of the time. No flag — analyst breaks down by
73
- * hour-of-day on deployment-completed events to discover the risk window.
74
- *
75
- * HOW TO FIND IT IN MIXPANEL:
76
- *
77
- * Report 1: Deploy Failure Rate by Hour of Day
78
- * - Report type: Insights
79
- * - Event: "deployment completed"
80
- * - Measure: Total
81
- * - Filter: deploy_status = "failed"
82
- * - Breakdown: hour of day
83
- * - Expected: 22:00-05:59 hours show ~ 40% failure vs ~ 15% baseline
84
- *
85
- * REAL-WORLD ANALOGUE: Night deploys fail more due to skeleton crews
86
- * and delayed incident response.
87
- *
88
- * ---------------------------------------------------------------
89
- * 3. COPILOT ADOPTION -> PR VELOCITY (everything hook)
90
- * ---------------------------------------------------------------
91
- *
92
- * PATTERN: Users with ai_assist="copilot" on any PR event get
93
- * 1.5x more pull_request_created events cloned into their stream.
94
- * AI-assisted developers ship more PRs.
95
- *
96
- * HOW TO FIND IT IN MIXPANEL:
97
- *
98
- * Report 1: PR Volume by AI Assist Mode
99
- * - Report type: Insights
100
- * - Event: "pull request created"
101
- * - Measure: Total per user (average)
102
- * - Breakdown: "ai_assist"
103
- * - Expected: copilot users ~1.5x more PRs than manual users
104
- *
105
- * REAL-WORLD ANALOGUE: AI coding assistants measurably increase
106
- * developer throughput, particularly for boilerplate and tests.
107
- *
108
- * ---------------------------------------------------------------
109
- * 4. ON-CALL ROTATION FATIGUE (everything hook)
110
- * ---------------------------------------------------------------
111
- *
112
- * PATTERN: Users with >20 alert_triggered events get increasing
113
- * response_time_minutes on incident events. Alert fatigue causes
114
- * slower response as on-call rotations grind engineers down.
115
- *
116
- * HOW TO FIND IT IN MIXPANEL:
117
- *
118
- * Report 1: Response Time vs Alert Volume
119
- * - Report type: Insights
120
- * - Event: "incident resolved"
121
- * - Measure: Average of "response_time_minutes"
122
- * - Filter: users with high alert counts
123
- * - Expected: heavy on-call users show 2-3x response time
124
- * (fatigued ~ 90min, normal ~ 30min)
125
- *
126
- * REAL-WORLD ANALOGUE: On-call burnout is a top cause of attrition
127
- * in SRE/DevOps. Alert fatigue degrades response quality over time.
128
- *
129
- * ---------------------------------------------------------------
130
- * 5. OPEN SOURCE POWER USAGE (everything hook)
131
- * ---------------------------------------------------------------
132
- *
133
- * PATTERN: OSS-segment users with >15 events get extra cloned build
134
- * + deploy events in their later activity (representing power usage).
135
- * Cloned events use unique offset timestamps. No flag — discover via
136
- * cohort by segment + event count, observing per-user build/deploy volume.
137
- *
138
- * HOW TO FIND IT IN MIXPANEL:
139
- *
140
- * Report 1: Builds per User — Active OSS Users
141
- * - Report type: Insights (with cohort)
142
- * - Cohort A: segment = "oss_user" AND events >= 15
143
- * - Cohort B: segment = "oss_user" AND events < 15
144
- * - Event: "build completed"
145
- * - Measure: Total per user
146
- * - Expected: A noticeably higher than B
147
- *
148
- * REAL-WORLD ANALOGUE: Power OSS users hit free-tier limits via
149
- * heavy build/deploy volume.
150
- *
151
- * ---------------------------------------------------------------
152
- * 6. POST-OUTAGE RECOVERY (everything hook)
153
- * ---------------------------------------------------------------
154
- *
155
- * PATTERN: After the major outage ends (day 42.25), deployment
156
- * events get a frequency boost -- extra cloned deployment events
157
- * represent the flurry of hotfixes and rollback-then-redeploy cycles.
158
- *
159
- * HOW TO FIND IT IN MIXPANEL:
160
- *
161
- * Report 1: Deployment Spike Post-Outage
162
- * - Report type: Insights
163
- * - Event: "deployment completed"
164
- * - Measure: Total
165
- * - Line chart by day
166
- * - Expected: visible spike in deploy volume days 43-46
167
- *
168
- * REAL-WORLD ANALOGUE: After a major outage, teams push a burst
169
- * of hotfixes, rollbacks, and emergency deploys to stabilize.
170
- *
171
- * ---------------------------------------------------------------
172
- * 7. DEVOPS LEAD PROFILE ENRICHMENT (user hook)
173
- * ---------------------------------------------------------------
174
- *
175
- * PATTERN: Users in the "devops" segment get team_size boosted
176
- * to 10-50 and repos_connected boosted to 5-20. DevOps leads
177
- * manage larger teams and more infrastructure.
178
- *
179
- * HOW TO FIND IT IN MIXPANEL:
180
- *
181
- * Report 1: Team Size by Segment
182
- * - Report type: Insights
183
- * - Event: any event
184
- * - Measure: Average of user property "team_size"
185
- * - Breakdown: user property "segment"
186
- * - Expected: devops ~ 30 avg, others ~ 10 avg
187
- *
188
- * REAL-WORLD ANALOGUE: DevOps leads oversee platform teams and
189
- * manage organization-wide CI/CD infrastructure.
190
- *
191
- * ---------------------------------------------------------------
192
- * 8. ENTERPRISE BUILD-DEPLOY FUNNEL LIFT (everything hook)
193
- * ---------------------------------------------------------------
194
- *
195
- * PATTERN: Free-tier users drop 35% of final funnel step events
196
- * ("monitoring dashboard viewed"), creating a visible conversion
197
- * gap between paid and free users. Enterprise/business users keep
198
- * all their events.
199
- *
200
- * HOW TO FIND IT IN MIXPANEL:
201
- *
202
- * Report 1: Build-Deploy Conversion by Tier
203
- * - Report type: Funnels
204
- * - Steps: "build completed" -> "deployment completed" -> "monitoring dashboard viewed"
205
- * - Breakdown: "subscription_tier" (superProp)
206
- * - Expected: enterprise ~ 60% vs free ~ 40% conversion
207
- *
208
- * REAL-WORLD ANALOGUE: Enterprise CI/CD customers get priority
209
- * runners, dedicated support, and SLA-backed uptime guarantees.
210
- *
211
- * ---------------------------------------------------------------
212
- * 9. BUILD-COUNT MAGIC NUMBER (everything hook)
213
- * ---------------------------------------------------------------
214
- *
215
- * PATTERN: Users with 15-30 "build completed" events sit in the
216
- * healthy CI sweet spot — 30% extra deploy events are cloned (unique
217
- * timestamps). Users with 31+ builds suffer flaky-CI burnout; ~25%
218
- * of their deploy events drop. No flag — discover by binning users
219
- * on build-count and comparing per-user deploy volume.
220
- *
221
- * HOW TO FIND IT IN MIXPANEL:
222
- *
223
- * Report 1: Deploys per User by Build Bucket
224
- * - Report type: Insights (with cohort)
225
- * - Cohort A: users with 15-30 "build completed"
226
- * - Cohort B: users with 0-14 "build completed"
227
- * - Event: "deployment completed"
228
- * - Measure: Total per user
229
- * - Expected: A ~ 1.3x B
230
- *
231
- * Report 2: Deploys per User on Heavy Builders
232
- * - Report type: Insights (with cohort)
233
- * - Cohort C: users with >= 31 "build completed"
234
- * - Cohort A: users with 15-30
235
- * - Event: "deployment completed"
236
- * - Measure: Total per user
237
- * - Expected: C ~ 25% fewer deploys per user vs A
238
- *
239
- * REAL-WORLD ANALOGUE: Healthy CI cadence drives reliable deploys;
240
- * runaway builds signal a flaky pipeline that scares teams off ships.
241
- *
242
- * ---------------------------------------------------------------
243
- * 10. BUILD-DEPLOY TIME-TO-CONVERT (funnel-post)
244
- * ---------------------------------------------------------------
245
- *
246
- * PATTERN: Enterprise and business tier users complete the Build-Deploy
247
- * Pipeline funnel (build completed -> deployment completed -> monitoring
248
- * dashboard viewed) 1.5x faster (factor 0.67). Free-tier users complete
249
- * it 1.33x slower (factor 1.33). The hook intercepts funnel-post arrays,
250
- * computes the time gap between consecutive steps, and scales each gap
251
- * by the tier-specific factor before rewriting the step timestamps.
252
- *
253
- * HOW TO FIND IT IN MIXPANEL:
254
- *
255
- * Report 1: Build-Deploy Pipeline Median TTC by Tier
256
- * - Report type: Funnels
257
- * - Steps: "build completed" -> "deployment completed" -> "monitoring dashboard viewed"
258
- * - Measure: Median time to convert
259
- * - Breakdown: "subscription_tier" (superProp)
260
- * - Expected: enterprise/business ~ 0.67x baseline; free ~ 1.33x baseline
261
- *
262
- * NOTE (funnel-post measurement): visible only via Mixpanel funnel
263
- * median TTC. Cross-event MIN→MIN SQL queries on raw events do NOT
264
- * show this — funnel-post adjusts gaps within funnel instances, not
265
- * across the user's full event history.
266
- *
267
- * REAL-WORLD ANALOGUE: Enterprise CI/CD customers get priority build
268
- * runners and dedicated deploy infrastructure, yielding faster
269
- * end-to-end pipeline throughput.
270
- *
271
- * ===============================================================
272
- * EXPECTED METRICS SUMMARY
273
- * ===============================================================
274
- *
275
- * Hook | Metric | Baseline | Effect | Ratio
276
- * ----------------------------|---------------------|----------|---------|------
277
- * Build Failure Cascade | build_duration_sec | 240s | 480s | 2x
278
- * Night Deploy Risk | deploy failure rate | 15% | 40% | 2.7x
279
- * Copilot PR Velocity | PRs/user | 3 | 4.5 | 1.5x
280
- * On-Call Fatigue | response_time_min | 30min | 90min | 3x
281
- * OSS Power Usage | builds/user (active)| 1x | ~ 1.3x | 1.3x
282
- * Post-Outage Recovery | deploys/day | 50 | 100+ | 2x+
283
- * DevOps Lead Profiles | team_size | 10 | 30 | 3x
284
- * Enterprise Funnel Lift | funnel conversion | 40% | 60% | ~ 1.5x
285
- * Build-Count Magic Number | sweet deploys/user | 1x | 1.3x | 1.3x
286
- * Build-Count Magic Number | over deploys/user | 1x | 0.75x | -25%
287
- * Build-Deploy TTC | median TTC by tier | 1x | 0.67/1.33x| ~ 2x range
288
- */
289
-
290
- // ── SCALE ──
291
- const SEED = "dm4-devtools";
292
- const NUM_USERS = 10_000;
293
- const DATASET_START = "2026-01-01T00:00:00Z";
294
- const DATASET_END = "2026-05-01T23:59:59Z";
295
- const EVENTS_PER_DAY = 1.2;
296
- const token = process.env.MP_TOKEN || "your-mixpanel-token";
297
-
298
- const chance = u.initChance(SEED);
299
-
300
- // ── KNOBS (tweak these to reshape stories) ──
301
- const BUILD_FAILURE_DURATION_MULT = 2;
302
-
303
- const NIGHT_DEPLOY_HOUR_START = 22;
304
- const NIGHT_DEPLOY_HOUR_END = 6;
305
- const NIGHT_DEPLOY_FAIL_LIKELIHOOD = 40;
306
-
307
- const COPILOT_USER_HASH_MOD = 10;
308
- const COPILOT_USER_HASH_THRESHOLD = 3;
309
- const COPILOT_PR_CLONE_RATE = 0.5;
310
-
311
- const ONCALL_ALERT_THRESHOLD = 20;
312
- const ONCALL_FATIGUE_DIVISOR = 20;
313
- const ONCALL_FATIGUE_CAP = 3;
314
-
315
- const OSS_EVENT_THRESHOLD = 15;
316
- const OSS_CONVERSION_POINT_PCT = 0.7;
317
- const OSS_BUILD_CLONE_LIKELIHOOD = 30;
318
- const OSS_DEPLOY_CLONE_LIKELIHOOD = 20;
319
-
320
- const RECOVERY_START_DAY = 44;
321
- const RECOVERY_END_DAY = 48;
322
- const RECOVERY_CLONES_PER_EVENT = 3;
323
-
324
- const ENTERPRISE_DROP_LIKELIHOOD = 35;
325
-
326
- const BUILD_SWEET_MIN = 15;
327
- const BUILD_SWEET_MAX = 30;
328
- const BUILD_OVER_THRESHOLD = 31;
329
- const BUILD_SWEET_CLONE_RATE = 0.5;
330
- const BUILD_OVER_DROP_LIKELIHOOD = 40;
331
-
332
- const TTC_FAST_FACTOR = 0.67;
333
- const TTC_SLOW_FACTOR = 1.33;
334
-
335
- // ── DATA ARRAYS ──
336
- // Generate consistent pipeline/repo IDs at module level
337
- const pipelineIds = v.range(1, 80).map(() => `PIPE_${v.uid(6)}`);
338
- const repoIds = v.range(1, 150).map(() => `REPO_${v.uid(6)}`);
339
-
340
- // ── HELPER FUNCTIONS ──
341
- function handleFunnelPostHooks(record, meta) {
342
- // H10: BUILD-DEPLOY TIME-TO-CONVERT
343
- // Enterprise tier completes Build-Deploy Pipeline funnel 1.5x faster
344
- // (factor 0.67); free tier 1.33x slower (factor 1.33).
345
- const segment = meta?.profile?.subscription_tier;
346
- if (Array.isArray(record) && record.length > 1) {
347
- const factor = (
348
- segment === "enterprise" || segment === "business" ? TTC_FAST_FACTOR :
349
- segment === "free" ? TTC_SLOW_FACTOR :
350
- 1.0
351
- );
352
- if (factor !== 1.0) {
353
- for (let i = 1; i < record.length; i++) {
354
- const prev = dayjs(record[i - 1].time);
355
- const newGap = Math.round(dayjs(record[i].time).diff(prev) * factor);
356
- record[i].time = prev.add(newGap, "milliseconds").toISOString();
357
- }
358
- }
359
- }
360
- return record;
361
- }
362
-
363
- function handleUserHooks(record) {
364
- // H7: DEVOPS LEAD PROFILE ENRICHMENT
365
- // DevOps leads get team_size 10-50 and repos_connected 5-20.
366
- // Platform engineers get moderate boosts. Others stay at defaults.
367
- if (record.segment === "devops") {
368
- record.team_size = chance.integer({ min: 10, max: 50 });
369
- record.repos_connected = chance.integer({ min: 5, max: 20 });
370
- record.experience_level = "senior";
371
- } else if (record.segment === "platform_eng") {
372
- record.team_size = chance.integer({ min: 5, max: 25 });
373
- record.repos_connected = chance.integer({ min: 3, max: 15 });
374
- record.experience_level = chance.pickone(["mid", "senior"]);
375
- } else if (record.segment === "junior") {
376
- record.team_size = chance.integer({ min: 1, max: 8 });
377
- record.repos_connected = chance.integer({ min: 0, max: 3 });
378
- record.experience_level = "junior";
379
- }
380
- return record;
381
- }
382
-
383
- function handleEventHooks(record) {
384
- // H1: BUILD FAILURE CASCADE
385
- // Failed builds get 2x duration (retries take longer).
386
- if (record.event === "build completed" && record.build_status === "failed") {
387
- record.build_duration_sec = Math.floor((record.build_duration_sec || 240) * BUILD_FAILURE_DURATION_MULT);
388
- }
389
- // (HOOK 2: NIGHT DEPLOY RISK moved to everything hook — hour checks
390
- // must run after bunchIntoSessions redistributes timestamps)
391
- return record;
392
- }
393
-
394
- function handleEverythingHooks(record, meta) {
395
- const datasetStart = dayjs.unix(meta.datasetStart);
396
- let events = record;
397
- if (!events.length) return record;
398
- const profile = meta && meta.profile ? meta.profile : {};
399
-
400
- // SUPERPROP STAMPING
401
- // Stamp superProp values from profile onto every event so they stay
402
- // consistent per-user instead of randomizing per-event.
403
- events.forEach(e => {
404
- if (profile.subscription_tier) e.subscription_tier = profile.subscription_tier;
405
- if (profile.Platform) e.Platform = profile.Platform;
406
- if (profile.language) e.language = profile.language;
407
- });
408
-
409
- // H2: NIGHT DEPLOY RISK
410
- // Deployments between 10PM-6AM get deploy_status forced to "failed"
411
- // 40% of the time. No flag — analyst breaks down by hour-of-day.
412
- events.forEach(e => {
413
- if (e.event === "deployment completed") {
414
- const hour = new Date(e.time).getUTCHours();
415
- if ((hour >= NIGHT_DEPLOY_HOUR_START || hour < NIGHT_DEPLOY_HOUR_END) && chance.bool({ likelihood: NIGHT_DEPLOY_FAIL_LIKELIHOOD })) {
416
- e.deploy_status = "failed";
417
- }
418
- }
419
- });
420
-
421
- // H8: ENTERPRISE BUILD-DEPLOY FUNNEL LIFT
422
- // Free-tier users drop 35% of final funnel step events to create
423
- // visible conversion gap vs paid subscribers.
424
- if (profile.subscription_tier !== "enterprise" && profile.subscription_tier !== "business") {
425
- events = events.filter(e => {
426
- if (e.event === "monitoring dashboard viewed" && chance.bool({ likelihood: ENTERPRISE_DROP_LIKELIHOOD })) return false;
427
- return true;
428
- });
429
- }
430
-
431
- // H3: COPILOT PR VELOCITY
432
- // ~30% of users are copilot adopters (hash-based cohort).
433
- // Copilot users get ai_assist="copilot" stamped and 1.5x more PRs.
434
- const uid = events[0]?.user_id || "";
435
- const isCopilotUser = (typeof uid === "string" ? uid.charCodeAt(0) : uid) % COPILOT_USER_HASH_MOD < COPILOT_USER_HASH_THRESHOLD;
436
- if (isCopilotUser) {
437
- events.forEach(e => {
438
- if (e.event === "pull request created" || e.event === "code review completed") {
439
- e.ai_assist = "copilot";
440
- }
441
- });
442
- const prEvents = events.filter(e => e.event === "pull request created");
443
- const extraCount = Math.floor(prEvents.length * COPILOT_PR_CLONE_RATE);
444
- for (let i = 0; i < extraCount; i++) {
445
- const templateEvent = prEvents[i % prEvents.length];
446
- if (templateEvent) {
447
- events.push({
448
- ...templateEvent,
449
- time: dayjs(templateEvent.time).add(chance.integer({ min: 1, max: 12 }), "hours").toISOString(),
450
- user_id: templateEvent.user_id,
451
- ai_assist: "copilot",
452
- files_changed: chance.integer({ min: 1, max: 30 }),
453
- lines_added: chance.integer({ min: 10, max: 800 }),
454
- });
455
- }
456
- }
457
- }
458
-
459
- // H4: ON-CALL ROTATION FATIGUE
460
- // Users with >20 alerts get increasing response_time_minutes.
461
- const alertCount = events.filter(e => e.event === "alert triggered").length;
462
- if (alertCount > ONCALL_ALERT_THRESHOLD) {
463
- const fatigueMultiplier = 1 + Math.min(alertCount / ONCALL_FATIGUE_DIVISOR, ONCALL_FATIGUE_CAP);
464
- events.forEach(e => {
465
- if (e.event === "incident resolved" && e.response_time_minutes) {
466
- e.response_time_minutes = Math.floor(e.response_time_minutes * fatigueMultiplier);
467
- }
468
- if (e.event === "incident created" && e.response_time_minutes) {
469
- e.response_time_minutes = Math.floor(e.response_time_minutes * fatigueMultiplier);
470
- }
471
- });
472
- }
473
-
474
- // H5: OPEN SOURCE POWER USAGE
475
- // OSS users with >15 events get extra cloned build + deploy events
476
- // in their later activity (representing power usage that pushes them
477
- // toward limits). No flag — discover via cohort by segment + event count.
478
- if (profile.segment === "oss_user" && events.length > OSS_EVENT_THRESHOLD) {
479
- const buildTemplate = events.find(e => e.event === "build completed");
480
- const deployTemplate = events.find(e => e.event === "deployment completed");
481
- if (buildTemplate || deployTemplate) {
482
- const conversionPoint = Math.floor(events.length * OSS_CONVERSION_POINT_PCT);
483
- const tail = events.slice(conversionPoint);
484
- tail.forEach(e => {
485
- const tBase = dayjs(e.time);
486
- if (buildTemplate && chance.bool({ likelihood: OSS_BUILD_CLONE_LIKELIHOOD })) {
487
- events.push({
488
- ...buildTemplate,
489
- time: tBase.add(chance.integer({ min: 5, max: 240 }), "minutes").toISOString(),
490
- user_id: e.user_id,
491
- });
492
- }
493
- if (deployTemplate && chance.bool({ likelihood: OSS_DEPLOY_CLONE_LIKELIHOOD })) {
494
- events.push({
495
- ...deployTemplate,
496
- time: tBase.add(chance.integer({ min: 10, max: 240 }), "minutes").toISOString(),
497
- user_id: e.user_id,
498
- });
499
- }
500
- });
501
- }
502
- }
503
-
504
- // H9: BUILD-COUNT MAGIC NUMBER (no flags)
505
- // Sweet 15-30 builds → +50% deploys (clone with unique offset).
506
- // Over 31+ → drop 40% of deploys (flaky CI burnout).
507
- const buildCount = events.filter(e => e.event === "build completed").length;
508
- if (buildCount >= BUILD_SWEET_MIN && buildCount <= BUILD_SWEET_MAX) {
509
- const deploys = events.filter(e => e.event === "deployment completed");
510
- const extras = Math.max(Math.floor(deploys.length * BUILD_SWEET_CLONE_RATE), 1);
511
- for (let k = 0; k < extras; k++) {
512
- const tpl = deploys[k % deploys.length];
513
- if (tpl) {
514
- events.push({
515
- ...tpl,
516
- time: dayjs(tpl.time).add(chance.integer({ min: 5, max: 360 }), "minutes").toISOString(),
517
- user_id: tpl.user_id,
518
- });
519
- }
520
- }
521
- } else if (buildCount >= BUILD_OVER_THRESHOLD) {
522
- for (let i = events.length - 1; i >= 0; i--) {
523
- if (events[i].event === "deployment completed" && chance.bool({ likelihood: BUILD_OVER_DROP_LIKELIHOOD })) {
524
- events.splice(i, 1);
525
- }
526
- }
527
- }
528
-
529
- // H6: POST-OUTAGE RECOVERY
530
- // After the outage volume rebound (d44-48), deployment events get
531
- // aggressively cloned to produce a visible spike above baseline.
532
- // Shifted later than outage end (d42.25) so natural volume has
533
- // recovered from the 0.05x suppression before cloning kicks in.
534
- const RECOVERY_START = datasetStart.add(RECOVERY_START_DAY, "days");
535
- const RECOVERY_END = datasetStart.add(RECOVERY_END_DAY, "days");
536
- const deployEvents = events.filter(e => {
537
- if (e.event !== "deployment completed") return false;
538
- const t = dayjs(e.time);
539
- return t.isAfter(RECOVERY_START) && t.isBefore(RECOVERY_END);
540
- });
541
- deployEvents.forEach(dep => {
542
- // 100% clone rate with 3 copies per event to clearly
543
- // exceed baseline deploy volume (d35-41)
544
- for (let c = 0; c < RECOVERY_CLONES_PER_EVENT; c++) {
545
- events.push({
546
- ...dep,
547
- time: dayjs(dep.time).add(chance.integer({ min: 1, max: 8 }), "hours").toISOString(),
548
- user_id: dep.user_id,
549
- deploy_status: chance.pickone(["success", "success", "rolled_back"]),
550
- environment: "production",
551
- });
552
- }
553
- });
554
-
555
- return events;
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: false,
577
- hasIOSDevices: false,
578
- hasDesktopDevices: true,
579
- hasBrowser: true,
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
- subscription_tier: {
592
- values: ["free", "pro", "enterprise"],
593
- frequency: "month",
594
- timing: "fuzzy",
595
- max: 6
596
- }
597
- },
598
- mirrorProps: {},
599
- lookupTables: [],
600
-
601
- // -- Events (18) ------------------------------------------
602
- events: [
603
- {
604
- event: "account created",
605
- weight: 1,
606
- isFirstEvent: true,
607
- isAuthEvent: true,
608
- properties: {
609
- referral_source: ["organic", "github", "conference", "blog_post", "colleague", "search"],
610
- },
611
- },
612
- {
613
- event: "build completed",
614
- weight: 8,
615
- isStrictEvent: false,
616
- properties: {
617
- pipeline_id: chance.pickone.bind(chance, pipelineIds),
618
- repo_id: chance.pickone.bind(chance, repoIds),
619
- build_status: ["success", "success", "success", "success", "failed", "cancelled"],
620
- build_duration_sec: u.weighNumRange(10, 600, 0.4, 240),
621
- branch: ["main", "main", "develop", "feature", "feature", "hotfix"],
622
- test_count: u.weighNumRange(10, 500, 0.5, 100),
623
- test_pass_rate: u.weighNumRange(70, 100, 0.8, 95),
624
- },
625
- },
626
- {
627
- event: "deployment completed",
628
- weight: 5,
629
- isStrictEvent: false,
630
- properties: {
631
- pipeline_id: chance.pickone.bind(chance, pipelineIds),
632
- repo_id: chance.pickone.bind(chance, repoIds),
633
- deploy_status: ["success", "success", "success", "failed", "rolled_back"],
634
- environment: ["production", "production", "staging", "staging", "dev", "preview"],
635
- deploy_duration_sec: u.weighNumRange(15, 300, 0.4, 90),
636
- preview_enabled: [false],
637
- },
638
- },
639
- {
640
- event: "pull request created",
641
- weight: 6,
642
- isStrictEvent: false,
643
- properties: {
644
- repo_id: chance.pickone.bind(chance, repoIds),
645
- pr_size: ["small", "small", "medium", "medium", "large", "xlarge"],
646
- files_changed: u.weighNumRange(1, 50, 0.4, 8),
647
- lines_added: u.weighNumRange(1, 2000, 0.3, 150),
648
- lines_removed: u.weighNumRange(0, 500, 0.3, 40),
649
- ai_assist: ["manual"],
650
- },
651
- },
652
- {
653
- event: "code review completed",
654
- weight: 5,
655
- isStrictEvent: false,
656
- properties: {
657
- repo_id: chance.pickone.bind(chance, repoIds),
658
- review_result: ["approved", "approved", "approved", "changes_requested", "commented"],
659
- review_duration_hours: u.weighNumRange(0.5, 72, 0.3, 8),
660
- comments_count: u.weighNumRange(0, 20, 0.5, 3),
661
- ai_assist: ["manual"],
662
- },
663
- },
664
- {
665
- event: "alert triggered",
666
- weight: 4,
667
- isStrictEvent: false,
668
- properties: {
669
- alert_type: ["error_rate", "latency", "cpu", "memory", "disk", "custom_metric"],
670
- severity: ["info", "warning", "warning", "critical", "critical"],
671
- service: ["api", "web", "worker", "database", "cdn", "auth"],
672
- acknowledged: [true, true, true, false],
673
- },
674
- },
675
- {
676
- event: "incident created",
677
- weight: 2,
678
- isStrictEvent: false,
679
- properties: {
680
- severity: ["sev1", "sev2", "sev2", "sev3", "sev3", "sev3"],
681
- service: ["api", "web", "worker", "database", "cdn", "auth"],
682
- response_time_minutes: u.weighNumRange(1, 120, 0.3, 30),
683
- root_cause: ["deploy", "config_change", "dependency", "traffic_spike", "hardware", "unknown"],
684
- },
685
- },
686
- {
687
- event: "incident resolved",
688
- weight: 2,
689
- isStrictEvent: false,
690
- properties: {
691
- severity: ["sev1", "sev2", "sev2", "sev3", "sev3", "sev3"],
692
- resolution_time_minutes: u.weighNumRange(5, 480, 0.3, 60),
693
- response_time_minutes: u.weighNumRange(1, 120, 0.3, 30),
694
- resolution_type: ["hotfix", "rollback", "config_change", "scaling", "restart", "manual"],
695
- },
696
- },
697
- {
698
- event: "repository connected",
699
- weight: 2,
700
- properties: {
701
- provider: ["github", "github", "github", "gitlab", "bitbucket"],
702
- repo_visibility: ["private", "private", "private", "public"],
703
- language: ["javascript", "python", "go", "rust", "java", "typescript"],
704
- },
705
- },
706
- {
707
- event: "pipeline configured",
708
- weight: 2,
709
- properties: {
710
- pipeline_id: chance.pickone.bind(chance, pipelineIds),
711
- pipeline_type: ["build_test", "build_test_deploy", "deploy_only", "lint_test"],
712
- trigger: ["push", "push", "pull_request", "schedule", "manual"],
713
- runtime: ["docker", "docker", "node", "python", "go"],
714
- },
715
- },
716
- {
717
- event: "collaboration invited",
718
- weight: 2,
719
- properties: {
720
- invite_role: ["developer", "developer", "admin", "viewer"],
721
- invite_method: ["email", "email", "link", "github_team"],
722
- },
723
- },
724
- {
725
- event: "monitoring dashboard viewed",
726
- weight: 5,
727
- isStrictEvent: false,
728
- properties: {
729
- dashboard_type: ["overview", "performance", "errors", "deploys", "custom"],
730
- time_range: ["1h", "6h", "24h", "7d", "30d"],
731
- widgets_count: u.weighNumRange(1, 12, 0.5, 4),
732
- },
733
- },
734
- {
735
- event: "log searched",
736
- weight: 4,
737
- properties: {
738
- query_type: ["full_text", "structured", "regex"],
739
- time_range: ["15m", "1h", "6h", "24h", "7d"],
740
- results_count: u.weighNumRange(0, 500, 0.3, 50),
741
- service: ["api", "web", "worker", "database", "auth"],
742
- },
743
- },
744
- {
745
- event: "notification received",
746
- weight: 6,
747
- properties: {
748
- notification_type: ["build_failed", "deploy_completed", "pr_review_requested", "alert_fired", "mention", "billing"],
749
- channel: ["in_app", "in_app", "email", "slack", "webhook"],
750
- opened: [true, true, true, false],
751
- },
752
- },
753
- {
754
- event: "billing updated",
755
- weight: 1,
756
- properties: {
757
- change_type: ["plan_upgrade", "plan_downgrade", "payment_method", "add_seats", "remove_seats"],
758
- payment_method: ["credit_card", "credit_card", "invoice", "paypal"],
759
- },
760
- },
761
- {
762
- event: "app session",
763
- weight: 8,
764
- properties: {
765
- session_duration_sec: u.weighNumRange(10, 3600, 0.4, 180),
766
- pages_viewed: u.weighNumRange(1, 20, 0.5, 4),
767
- },
768
- },
769
- {
770
- event: "environment created",
771
- weight: 2,
772
- properties: {
773
- env_type: ["production", "staging", "dev", "preview", "test"],
774
- cloud_provider: ["aws", "aws", "gcp", "azure", "self_hosted"],
775
- region: ["us-east-1", "us-west-2", "eu-west-1", "ap-south-1"],
776
- },
777
- },
778
- {
779
- event: "account deactivated",
780
- weight: 1,
781
- isChurnEvent: true,
782
- returnLikelihood: 0.15,
783
- isStrictEvent: true,
784
- properties: {
785
- reason: ["switched_provider", "cost", "no_longer_needed", "poor_experience", "team_dissolved", "acquired"],
786
- },
787
- },
788
- ],
789
-
790
- // -- Funnels (5) ------------------------------------------
791
- funnels: [
792
- {
793
- name: "Onboarding",
794
- sequence: ["account created", "repository connected", "pipeline configured", "build completed"],
795
- conversionRate: 45,
796
- order: "sequential",
797
- isFirstFunnel: true,
798
- timeToConvert: 72,
799
- weight: 3,
800
- },
801
- {
802
- name: "Build-Deploy Pipeline",
803
- sequence: ["build completed", "deployment completed", "monitoring dashboard viewed"],
804
- conversionRate: 40,
805
- order: "sequential",
806
- timeToConvert: 48,
807
- weight: 5,
808
- reentry: true,
809
- },
810
- {
811
- name: "PR Review Flow",
812
- sequence: ["pull request created", "code review completed", "build completed", "deployment completed"],
813
- conversionRate: 35,
814
- order: "sequential",
815
- timeToConvert: 72,
816
- weight: 4,
817
- },
818
- {
819
- name: "Incident Response",
820
- sequence: ["alert triggered", "incident created", "incident resolved"],
821
- conversionRate: 50,
822
- order: "sequential",
823
- timeToConvert: 24,
824
- weight: 3,
825
- reentry: true,
826
- },
827
- {
828
- name: "Upgrade Path",
829
- sequence: ["app session", "billing updated", "collaboration invited"],
830
- conversionRate: 20,
831
- order: "sequential",
832
- timeToConvert: 168,
833
- weight: 2,
834
- },
835
- ],
836
-
837
- // -- SuperProps --------------------------------------------
838
- superProps: {
839
- subscription_tier: ["free", "free", "free", "team", "team", "business", "enterprise"],
840
- Platform: ["web", "web", "desktop_app", "cli"],
841
- language: ["javascript", "python", "go", "rust", "java", "typescript"],
842
- },
843
-
844
- // -- UserProps ---------------------------------------------
845
- userProps: {
846
- dev_role: ["full_stack"],
847
- segment: ["full_stack"],
848
- team_size: u.weighNumRange(1, 50, 0.4, 5),
849
- repos_connected: [0],
850
- org_name: ["personal"],
851
- experience_level: ["junior", "junior", "mid", "mid", "mid", "senior", "senior"],
852
- subscription_tier: ["free", "free", "free", "team", "team", "business", "enterprise"],
853
- Platform: ["web", "web", "desktop_app", "cli"],
854
- language: ["javascript", "python", "go", "rust", "java", "typescript"],
855
- },
856
-
857
- // -- Phase 2: Personas ------------------------------------
858
- personas: [
859
- {
860
- name: "platform_engineer",
861
- weight: 15,
862
- eventMultiplier: 4.0,
863
- conversionModifier: 1.5,
864
- churnRate: 0.01,
865
- properties: {
866
- dev_role: "platform_engineer",
867
- segment: "platform_eng",
868
- },
869
- },
870
- {
871
- name: "full_stack_dev",
872
- weight: 35,
873
- eventMultiplier: 1.5,
874
- conversionModifier: 1.0,
875
- churnRate: 0.05,
876
- properties: {
877
- dev_role: "full_stack",
878
- segment: "full_stack",
879
- },
880
- },
881
- {
882
- name: "junior_dev",
883
- weight: 30,
884
- eventMultiplier: 0.8,
885
- conversionModifier: 0.6,
886
- churnRate: 0.12,
887
- properties: {
888
- dev_role: "junior",
889
- segment: "junior",
890
- },
891
- },
892
- {
893
- name: "devops_lead",
894
- weight: 10,
895
- eventMultiplier: 2.0,
896
- conversionModifier: 1.3,
897
- churnRate: 0.02,
898
- properties: {
899
- dev_role: "devops_lead",
900
- segment: "devops",
901
- },
902
- },
903
- {
904
- name: "open_source_user",
905
- weight: 10,
906
- eventMultiplier: 0.5,
907
- conversionModifier: 0.3,
908
- churnRate: 0.15,
909
- properties: {
910
- dev_role: "open_source",
911
- segment: "oss_user",
912
- },
913
- },
914
- ],
915
-
916
- // -- Phase 2: World Events --------------------------------
917
- worldEvents: [
918
- {
919
- name: "major_outage",
920
- startDay: 42,
921
- duration: 0.25,
922
- volumeMultiplier: 0.05,
923
- affectsEvents: ["deployment completed", "build completed"],
924
- injectProps: { outage_window: true },
925
- },
926
- {
927
- name: "conference_launch",
928
- startDay: 60,
929
- duration: 3,
930
- volumeMultiplier: 2.0,
931
- affectsEvents: ["account created"],
932
- injectProps: { promo: "devcon2024" },
933
- },
934
- ],
935
-
936
- // -- Phase 2: Data Quality --------------------------------
937
- dataQuality: {
938
- lateArrivingRate: 0.01,
939
- duplicateRate: 0.005,
940
- botUsers: 2,
941
- botEventsPerUser: 300,
942
- },
943
-
944
- // -- Phase 2: Subscription --------------------------------
945
- subscription: {
946
- plans: [
947
- { name: "free", price: 0, default: true },
948
- { name: "team", price: 25, trialDays: 14 },
949
- { name: "business", price: 75 },
950
- { name: "enterprise", price: 200 },
951
- ],
952
- lifecycle: {
953
- trialToPayRate: 0.30,
954
- upgradeRate: 0.08,
955
- downgradeRate: 0.03,
956
- churnRate: 0.05,
957
- winBackRate: 0.10,
958
- winBackDelay: 21,
959
- paymentFailureRate: 0.02,
960
- },
961
- },
962
-
963
- // -- Phase 2: Geo -----------------------------------------
964
- geo: {
965
- sticky: true,
966
- regions: [
967
- {
968
- name: "us",
969
- countries: ["US"],
970
- weight: 40,
971
- timezoneOffset: -5,
972
- properties: { currency: "USD", locale: "en-US" },
973
- },
974
- {
975
- name: "eu",
976
- countries: ["GB", "DE", "FR", "NL"],
977
- weight: 30,
978
- timezoneOffset: 1,
979
- properties: { currency: "EUR", locale: "en-EU" },
980
- },
981
- {
982
- name: "india",
983
- countries: ["IN"],
984
- weight: 15,
985
- timezoneOffset: 5.5,
986
- properties: { currency: "INR", locale: "en-IN" },
987
- },
988
- {
989
- name: "rest",
990
- countries: ["SG", "AU", "JP"],
991
- weight: 15,
992
- timezoneOffset: 8,
993
- properties: { currency: "SGD", locale: "en-SG" },
994
- },
995
- ],
996
- },
997
-
998
- // -- Phase 2: Features ------------------------------------
999
- features: [
1000
- {
1001
- name: "copilot_integration",
1002
- launchDay: 30,
1003
- adoptionCurve: "fast",
1004
- property: "ai_assist",
1005
- values: ["manual", "copilot"],
1006
- defaultBefore: "manual",
1007
- affectsEvents: ["pull request created", "code review completed"],
1008
- },
1009
- {
1010
- name: "deployment_preview",
1011
- launchDay: 50,
1012
- adoptionCurve: { k: 0.08, midpoint: 25 },
1013
- property: "preview_enabled",
1014
- values: [false, true],
1015
- defaultBefore: false,
1016
- affectsEvents: ["deployment completed"],
1017
- },
1018
- ],
1019
-
1020
- // -- Phase 2: Anomalies -----------------------------------
1021
- anomalies: {
1022
- extremeValues: [
1023
- {
1024
- event: "build completed",
1025
- property: "build_duration_sec",
1026
- frequency: 0.003,
1027
- multiplier: 10,
1028
- },
1029
- ],
1030
- bursts: [
1031
- {
1032
- event: "alert triggered",
1033
- day: 42,
1034
- duration: 0.08,
1035
- count: 500,
1036
- injectProps: { alert_tag: "outage_alerts" },
1037
- },
1038
- ],
1039
- },
1040
-
1041
- hook(record, type, meta) {
1042
- if (type === "funnel-post") return handleFunnelPostHooks(record, meta);
1043
- if (type === "user") return handleUserHooks(record);
1044
- if (type === "event") return handleEventHooks(record);
1045
- if (type === "everything") return handleEverythingHooks(record, meta);
1046
- return record;
1047
- },
1048
- };
1049
-
1050
- export default config;