@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,995 +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
- import { findFirstSequence, scaleFunnelTTC } from "../../lib/hook-helpers/timing.js";
9
- /** @typedef {import("../../types").Dungeon} Config */
10
-
11
- // ── OVERVIEW ──
12
- /*
13
- * NAME: CloudForge
14
- * APP: B2B SaaS that fuses infrastructure monitoring (Datadog-style) with
15
- * deployment automation (Terraform-style). Engineering teams create
16
- * workspaces, deploy services across AWS/GCP/Azure, monitor uptime
17
- * and cost, and respond to alerts via Slack/PagerDuty runbooks.
18
- * Pricing: Free / Team / Business / Enterprise (seats + usage).
19
- * SCALE: 10,000 users, ~1.4M events, 121 days (2026-01-01 → 2026-05-01)
20
- * CORE LOOP: workspace created → service deployed → dashboard viewed → alert/resolve
21
- *
22
- * EVENTS (19):
23
- * dashboard viewed (20) > api call (16) > query executed (15) > alert triggered (12)
24
- * > service deployed (10) > deployment pipeline run (9) > alert acknowledged (8)
25
- * > alert resolved (7) > documentation viewed (7) > security scan (6)
26
- * > infrastructure scaled (5) > cost report generated (4) > integration configured (4)
27
- * > feature flag toggled (4) > team member invited (3) > runbook executed (3)
28
- * > billing event (3) > workspace created (1) > incident created (1)
29
- *
30
- * FUNNELS (8):
31
- * - Onboarding: workspace created → service deployed → dashboard viewed (70%)
32
- * - Daily Monitoring: dashboard viewed → query executed → api call (80%)
33
- * - Incident Response: alert triggered → alert acknowledged → alert resolved (55%)
34
- * - Deployment: deployment pipeline run → service deployed → dashboard viewed (65%, Canary A/B)
35
- * - Infrastructure Mgmt: cost report generated → infrastructure scaled → security scan (50%)
36
- * - Team & Config: team member invited → integration configured → feature flag toggled (40%)
37
- * - Docs & Runbooks: documentation viewed → runbook executed → service deployed (45%)
38
- * - Billing: billing event → dashboard viewed (60%)
39
- *
40
- * USER PROPS: company_size, primary_role, team_name, seat_count, annual_contract_value,
41
- * customer_success_manager, customer_health_score, plan_tier, cloud_provider
42
- * SUPER PROPS: plan_tier, cloud_provider
43
- * SCD PROPS: primary_role (viewer/editor/admin/owner, monthly fuzzy, max 6),
44
- * plan_tier (starter/growth/enterprise/scale, monthly fixed, max 6, company_id-scoped)
45
- * GROUPS: company_id (300 companies)
46
- */
47
-
48
- // ── HOOK STORIES ──
49
- /*
50
- * NOTE: All cohort effects are HIDDEN — no flag stamping. Discoverable via
51
- * behavioral cohorts or raw-prop breakdowns (company_size, day, doc_section).
52
- *
53
- * ─────────────────────────────────────────────────────────────────────────────
54
- * 1. END-OF-QUARTER SPIKE (everything)
55
- * ─────────────────────────────────────────────────────────────────────────────
56
- *
57
- * PATTERN: Days 100-110 billing events shift event_type toward "plan_upgraded"
58
- * 40% of the time and team member invitations are duplicated 50% of the time.
59
- * No flag — discover via line chart by day.
60
- *
61
- * HOW TO FIND IT IN MIXPANEL:
62
- *
63
- * Report 1: Plan Upgrades Over Time
64
- * - Report type: Insights
65
- * - Event: "billing event"
66
- * - Measure: Total
67
- * - Filter: event_type = "plan_upgraded"
68
- * - Line chart by day
69
- * - Expected: ~4x normal upgrade volume during days 100-110
70
- *
71
- * Report 2: Team Expansion Surge
72
- * - Report type: Insights
73
- * - Event: "team member invited"
74
- * - Measure: Total
75
- * - Line chart by day
76
- * - Expected: clear volume spike in the final 10 days from duplicated invites
77
- *
78
- * REAL-WORLD ANALOGUE: B2B SaaS revenue clusters at quarter-close as sales
79
- * teams pull deals forward and customers expand seats to lock in pricing.
80
- *
81
- * ─────────────────────────────────────────────────────────────────────────────
82
- * 2. CHURNED ACCOUNT SILENCING (everything)
83
- * ─────────────────────────────────────────────────────────────────────────────
84
- *
85
- * PATTERN: ~10-20% of users (deterministic via distinct_id char hash) go
86
- * completely silent after day 30. All post-d30 events are removed via splice().
87
- * No flag — derive cohort via behavioral retention bucket (users with zero
88
- * activity past d30 vs the rest).
89
- *
90
- * HOW TO FIND IT IN MIXPANEL:
91
- *
92
- * Report 1: Retention Cliff
93
- * - Cohort A: users with at least 1 event AFTER day 30
94
- * - Cohort B: users with events ONLY in days 1-30
95
- * - Compare cohort sizes — B should be ~10-20% of total
96
- *
97
- * Report 2: Activity Volume Pre/Post Day 30
98
- * - Report type: Insights
99
- * - Event: any event
100
- * - Measure: Total per user
101
- * - Line chart by day
102
- * - Expected: a visible drop after d30 driven by the silent cohort
103
- *
104
- * REAL-WORLD ANALOGUE: Most SaaS churn happens silently — accounts simply
105
- * stop logging in long before the formal cancellation lands.
106
- *
107
- * ─────────────────────────────────────────────────────────────────────────────
108
- * 3. ALERT ESCALATION REPLACEMENT (event)
109
- * ─────────────────────────────────────────────────────────────────────────────
110
- *
111
- * PATTERN: 30% of critical/emergency "alert triggered" events are REPLACED
112
- * with a hook-only "incident created" event (not in the events array). The
113
- * new event carries escalation_level (P1/P2), teams_paged, and incident_id.
114
- *
115
- * HOW TO FIND IT IN MIXPANEL:
116
- *
117
- * Report 1: Incident Created Discovery
118
- * - Report type: Insights
119
- * - Event: "incident created"
120
- * - Measure: Total
121
- * - Breakdown: "escalation_level"
122
- * - Expected: P1 and P2 incidents, ~30% of critical/emergency alert volume
123
- *
124
- * Report 2: Alert vs Incident Ratio
125
- * - Report type: Insights
126
- * - Events: "alert triggered" AND "incident created"
127
- * - Measure: Total
128
- * - Expected: incident count ~ 30% of critical+emergency alert count
129
- *
130
- * REAL-WORLD ANALOGUE: Severe alerts get auto-promoted into incident
131
- * tickets that page on-call engineers and trigger customer comms.
132
- *
133
- * ─────────────────────────────────────────────────────────────────────────────
134
- * 4. INTEGRATION USERS SUCCEED (everything)
135
- * ─────────────────────────────────────────────────────────────────────────────
136
- *
137
- * PATTERN: Users with BOTH "slack" AND "pagerduty" "integration configured"
138
- * events resolve alerts faster: response_time_mins reduced 60%, resolution_time_mins
139
- * reduced 50%. No flag — derive cohort behaviorally.
140
- *
141
- * HOW TO FIND IT IN MIXPANEL:
142
- *
143
- * Report 1: Response Time by Integration Cohort
144
- * - Cohort A: users who configured BOTH slack AND pagerduty integrations
145
- * - Cohort B: rest
146
- * - Event: "alert acknowledged"
147
- * - Measure: Average of "response_time_mins"
148
- * - Expected: A ~ 60% lower response time
149
- *
150
- * Report 2: Resolution Time by Integration Cohort
151
- * - Cohort A vs B (as above)
152
- * - Event: "alert resolved"
153
- * - Measure: Average of "resolution_time_mins"
154
- * - Expected: A ~ 50% faster resolution
155
- *
156
- * REAL-WORLD ANALOGUE: Teams that wire alerting into their existing comms
157
- * stack respond minutes faster — the alert literally finds the human.
158
- *
159
- * ─────────────────────────────────────────────────────────────────────────────
160
- * 5. DOCS READERS DEPLOY MORE (everything)
161
- * ─────────────────────────────────────────────────────────────────────────────
162
- *
163
- * PATTERN: Users with 3+ "documentation viewed" events where doc_section
164
- * (or equivalent prop) indicates best-practices reading get 2-3 extra
165
- * production deploys spliced in. No flag — derive cohort by counting
166
- * doc views per user.
167
- *
168
- * HOW TO FIND IT IN MIXPANEL:
169
- *
170
- * Report 1: Per-User Deploy Volume by Docs Cohort
171
- * - Cohort A: users with >= 3 "documentation viewed" events
172
- * - Cohort B: users with < 3
173
- * - Event: "service deployed"
174
- * - Measure: Total per user
175
- * - Expected: A ~ 1.8x B
176
- *
177
- * REAL-WORLD ANALOGUE: Engineers who read the docs ship more confidently
178
- * and more often than those who guess at the platform.
179
- *
180
- * ─────────────────────────────────────────────────────────────────────────────
181
- * 6. COST OVERRUN PATTERN (event — closure state)
182
- * ─────────────────────────────────────────────────────────────────────────────
183
- *
184
- * PATTERN: When cost_change_percent > 25 on a "cost report generated" event,
185
- * the user is stored in a module-level Map. Their next "infrastructure scaled"
186
- * event is forced to scale_direction = "down". No flag — discover by
187
- * sequencing cost-report → infrastructure-scaled per user.
188
- *
189
- * HOW TO FIND IT IN MIXPANEL:
190
- *
191
- * Report 1: Scale Direction Distribution
192
- * - Report type: Insights
193
- * - Event: "infrastructure scaled"
194
- * - Measure: Total
195
- * - Breakdown: "scale_direction"
196
- * - Expected: "down" share is elevated above the configured baseline
197
- *
198
- * Report 2: Sequencing Check
199
- * - Inspect users with cost_change_percent > 25 on cost report;
200
- * their next "infrastructure scaled" should be scale_direction="down"
201
- * - Expected: ~100% match for the next-scale event after a cost spike
202
- *
203
- * REAL-WORLD ANALOGUE: A surprise cloud bill triggers an immediate
204
- * downscale; no engineer ignores a 25% month-over-month cost jump.
205
- *
206
- * ─────────────────────────────────────────────────────────────────────────────
207
- * 7. FAILED DEPLOYMENT RECOVERY (everything)
208
- * ─────────────────────────────────────────────────────────────────────────────
209
- *
210
- * PATTERN: After a failed pipeline run, the user's next successful deploy has
211
- * duration_sec * 1.5 (recovery deploys are slower). No flag — discover by
212
- * sequencing failed → next-success pipeline events per user and comparing
213
- * duration.
214
- *
215
- * HOW TO FIND IT IN MIXPANEL:
216
- *
217
- * Report 1: Pipeline Duration After Failure (sequencing query)
218
- * - For each user, find runs where prior run was status="failed"
219
- * - Compare avg duration_sec of those "next" runs vs all other successful runs
220
- * - Expected: post-failure runs ~ 1.5x longer duration
221
- *
222
- * REAL-WORLD ANALOGUE: After a bad deploy, teams add manual gates and
223
- * extra verification steps that slow the very next release.
224
- *
225
- * ─────────────────────────────────────────────────────────────────────────────
226
- * 8. ENTERPRISE VS STARTUP (user)
227
- * ─────────────────────────────────────────────────────────────────────────────
228
- *
229
- * PATTERN: Company size determines seat_count, annual_contract_value, and
230
- * customer_success_manager (enterprise only). All users get a
231
- * customer_health_score on the profile.
232
- *
233
- * HOW TO FIND IT IN MIXPANEL:
234
- *
235
- * Report 1: ACV by Company Size
236
- * - Report type: Insights
237
- * - Event: any event
238
- * - Measure: Unique users
239
- * - Breakdown: "company_size" (user property)
240
- * - Expected: startup ($0-3.6K), smb ($3.6K-12K), mid_market ($12K-50K),
241
- * enterprise ($50K-500K)
242
- *
243
- * Report 2: Seat Count by Company Size
244
- * - Report type: Insights
245
- * - Event: any event
246
- * - Measure: Average of "seat_count" (user property)
247
- * - Breakdown: "company_size"
248
- * - Expected: monotonic ramp from startup to enterprise
249
- *
250
- * REAL-WORLD ANALOGUE: B2B SaaS pricing scales orders of magnitude across
251
- * customer segments — from a $99/mo startup to a $500K Fortune 500 contract.
252
- *
253
- * ─────────────────────────────────────────────────────────────────────────────
254
- * 9. INCIDENT RESPONSE TTC (everything)
255
- * ─────────────────────────────────────────────────────────────────────────────
256
- *
257
- * PATTERN: Enterprise companies resolve incidents faster; startups resolve
258
- * slower. The hook reads company_size from the user profile and scales
259
- * response_time_mins (on "alert acknowledged") and resolution_time_mins
260
- * (on "alert resolved") by a factor: enterprise 0.67x, startup 1.5x,
261
- * smb/mid_market unchanged. Additionally, the first incident-response
262
- * funnel sequence (alert triggered → alert acknowledged → alert resolved)
263
- * has its inter-step timestamps scaled via scaleFunnelTTC with the same
264
- * factor, so Mixpanel funnel TTC reports reflect the gap. This compounds
265
- * with Hook 4 (integration users) — an enterprise user with both Slack
266
- * and PagerDuty configured gets the fastest resolution times.
267
- *
268
- * HOW TO FIND IT IN MIXPANEL:
269
- *
270
- * Report 1: Avg Response Time by Company Size
271
- * - Report type: Insights
272
- * - Event: "alert acknowledged"
273
- * - Measure: Average of "response_time_mins"
274
- * - Breakdown: "company_size" (user property)
275
- * - Expected: enterprise ~ 0.67x startup
276
- *
277
- * Report 2: Incident Funnel TTC by Company Size
278
- * - Report type: Funnels
279
- * - Steps: "alert triggered" → "alert acknowledged" → "alert resolved"
280
- * - Measure: Median time to convert
281
- * - Breakdown: "company_size"
282
- * - Expected: enterprise ~ 0.67x median TTC vs startup ~ 1.5x
283
- *
284
- * Report 3: Avg Resolution Time by Company Size
285
- * - Report type: Insights
286
- * - Event: "alert resolved"
287
- * - Measure: Average of "resolution_time_mins"
288
- * - Breakdown: "company_size"
289
- * - Expected: enterprise ~ 0.67x startup
290
- *
291
- * REAL-WORLD ANALOGUE: Enterprise teams have dedicated SRE rotations,
292
- * automated runbooks, and premium support contracts that compress
293
- * incident timelines. Startups rely on smaller teams with less tooling.
294
- *
295
- * ─────────────────────────────────────────────────────────────────────────────
296
- * 10. DOCS MAGIC NUMBER (everything)
297
- * ─────────────────────────────────────────────────────────────────────────────
298
- *
299
- * PATTERN: Users in the 4-7 documentation-viewed sweet spot get 2-3 extra
300
- * production "service deployed" events cloned into their stream (boosting
301
- * deploy frequency ~40%). Users with 8+ documentation views are over-
302
- * engaged browsers; 25% of their "service deployed" events are dropped.
303
- * No flag is stamped — discoverable only by binning users on doc-view
304
- * count and comparing per-user deploy volume.
305
- *
306
- * HOW TO FIND IT IN MIXPANEL:
307
- *
308
- * Report 1: Deploys per User by Docs-View Bucket
309
- * - Report type: Insights (with cohort)
310
- * - Cohort A: users with 4-7 "documentation viewed" events
311
- * - Cohort B: users with 0-3 "documentation viewed" events
312
- * - Event: "service deployed"
313
- * - Measure: Total per user
314
- * - Expected: Cohort A ~ 1.4x deploys per user vs Cohort B
315
- *
316
- * Report 2: Deploys per User on Heavy Doc Readers
317
- * - Report type: Insights (with cohort)
318
- * - Cohort C: users with >= 8 "documentation viewed" events
319
- * - Cohort A: users with 4-7
320
- * - Event: "service deployed"
321
- * - Measure: Total per user
322
- * - Expected: Cohort C ~ 25% fewer deploys per user vs Cohort A
323
- *
324
- * REAL-WORLD ANALOGUE: Engineers who read just enough docs deploy with
325
- * confidence; those who read excessively may be stuck troubleshooting
326
- * and never ship, or are evaluating the product without committing.
327
- *
328
- * ─────────────────────────────────────────────────────────────────────────────
329
- * 11. DEPLOY PIPELINE EXPERIMENT (funnel experiment — engine-managed)
330
- * ─────────────────────────────────────────────────────────────────────────────
331
- *
332
- * PATTERN: The deployment funnel (deployment pipeline run → service deployed
333
- * → dashboard viewed) runs a "Canary Deploys" experiment starting 45 days
334
- * before dataset end. Users are randomly assigned to Control or "Canary
335
- * Deploys" variant. The Canary variant gets 1.2x conversion multiplier and
336
- * 0.85x time-to-convert multiplier (faster + higher conversion). The engine
337
- * emits `$experiment_started` events with `Experiment Name` and
338
- * `Variant Name` properties. No hook code needed — the engine handles
339
- * variant assignment and conversion/TTC scaling.
340
- *
341
- * HOW TO FIND IT IN MIXPANEL:
342
- *
343
- * Report 1: Experiment Enrollment
344
- * - Report type: Insights
345
- * - Event: "$experiment_started"
346
- * - Measure: Total
347
- * - Breakdown: "Variant Name"
348
- * - Expected: roughly even split between "Control" and "Canary Deploys"
349
- *
350
- * Report 2: Deploy Funnel by Variant
351
- * - Report type: Funnels
352
- * - Steps: "deployment pipeline run" → "service deployed" → "dashboard viewed"
353
- * - Breakdown: "Variant Name" (user property)
354
- * - Expected: Canary variant ~ 1.2x conversion vs Control
355
- *
356
- * Report 3: Deploy TTC by Variant
357
- * - Report type: Funnels
358
- * - Steps: same as above
359
- * - Measure: Median time to convert
360
- * - Breakdown: "Variant Name"
361
- * - Expected: Canary variant ~ 0.85x TTC vs Control (faster)
362
- *
363
- * REAL-WORLD ANALOGUE: Teams A/B test canary deployment strategies.
364
- * Canary deploys catch issues earlier, improving both success rate and
365
- * deployment velocity.
366
- *
367
- * ─────────────────────────────────────────────────────────────────────────────
368
- * EXPECTED METRICS SUMMARY
369
- * ─────────────────────────────────────────────────────────────────────────────
370
- *
371
- * Hook | Metric | Baseline | Hook Effect | Ratio
372
- * -------------------------|--------------------------|-----------|----------------|------
373
- * End-of-Quarter Spike | Plan upgrades/day | ~2/day | ~8/day | 4x
374
- * Churned Accounts | Users active month 2 | 100% | 90% | 0.9x
375
- * Alert Escalation | Incidents from alerts | 0% | ~30% of crit | new
376
- * Integration Users | MTTR (minutes) | ~300 | ~150 | 0.5x
377
- * Docs Readers | Prod deploys/user | ~3 | ~5-6 | 1.8x
378
- * Cost Overrun | Scale-down after overrun | 50% | 100% | 2x
379
- * Failed Deploy Recovery | Deploy duration (sec) | ~500 | ~750 | 1.5x
380
- * Enterprise vs Startup | ACV range | $0-3.6K | $50K-500K | 100x+
381
- * Incident Response TTC | Enterprise response_time | 1x | 0.67x | -33%
382
- * Incident Response TTC | Startup response_time | 1x | 1.5x | +50%
383
- * Docs Magic Number | sweet (4-7) deploys/user | 1x | ~1.4x | +40%
384
- * Docs Magic Number | over (8+) deploys/user | 1x | ~0.75x | -25%
385
- * Deploy Experiment | Canary conversion | 65% | ~78% | 1.2x
386
- * Deploy Experiment | Canary TTC | 1d | ~0.85d | 0.85x
387
- */
388
-
389
- // ── SCALE ──
390
- const SEED = "harness-sass";
391
- const NUM_USERS = 10_000;
392
- const DATASET_START = "2026-01-01T00:00:00Z";
393
- const DATASET_END = "2026-05-01T23:59:59Z";
394
- const EVENTS_PER_DAY = 1.2;
395
- const token = process.env.MP_TOKEN || "your-mixpanel-token";
396
-
397
- const chance = u.initChance(SEED);
398
-
399
- // ── KNOBS (tweak these to reshape stories) ──
400
- const EOQ_START_DAY = 100;
401
- const EOQ_END_DAY = 110;
402
- const EOQ_UPGRADE_LIKELIHOOD = 40;
403
- const EOQ_INVITE_CLONE_LIKELIHOOD = 50;
404
-
405
- const CHURN_USER_HASH_MOD = 5;
406
- const CHURN_CUTOFF_DAYS = 30;
407
-
408
- const ALERT_ESCALATION_LIKELIHOOD = 30;
409
-
410
- const INTEGRATION_RESPONSE_FACTOR = 0.4;
411
- const INTEGRATION_RESOLUTION_FACTOR = 0.5;
412
-
413
- const COST_OVERRUN_THRESHOLD = 25;
414
-
415
- const FAILED_DEPLOY_RECOVERY_MULT = 1.5;
416
-
417
- const DOCS_SWEET_MIN = 4;
418
- const DOCS_SWEET_MAX = 7;
419
- const DOCS_OVER_THRESHOLD = 8;
420
- const DOCS_EXTRA_DEPLOYS_MIN = 2;
421
- const DOCS_EXTRA_DEPLOYS_MAX = 3;
422
- const DOCS_DEPLOY_DROP_LIKELIHOOD = 25;
423
-
424
- const TTC_ENTERPRISE_FACTOR = 0.67;
425
- const TTC_STARTUP_FACTOR = 1.5;
426
- const INCIDENT_SEQ_WINDOW_MINS = 60 * 24 * 30;
427
-
428
- // ── DATA ARRAYS ──
429
- const serviceIds = v.range(1, 201).map(() => `svc_${v.uid(8)}`);
430
- const alertIds = v.range(1, 501).map(() => `alert_${v.uid(6)}`);
431
- const pipelineIds = v.range(1, 101).map(() => `pipe_${v.uid(6)}`);
432
- const runbookIds = v.range(1, 51).map(() => `rb_${v.uid(6)}`);
433
-
434
- // ── HOOK STATE ──
435
- // Module-level Map for closure-based state tracking across event-hook calls
436
- const costOverrunUsers = new Map();
437
-
438
- // ── HELPER FUNCTIONS ──
439
- function handleEventHooks(record) {
440
- // H3: ALERT ESCALATION REPLACEMENT — critical/emergency alerts sometimes
441
- // become incident-created events.
442
- if (record.event === "alert triggered") {
443
- const severity = record.severity;
444
- if ((severity === "critical" || severity === "emergency") && chance.bool({ likelihood: ALERT_ESCALATION_LIKELIHOOD })) {
445
- return {
446
- ...record,
447
- event: "incident created",
448
- escalation_level: chance.pickone(["P1", "P2"]),
449
- teams_paged: chance.integer({ min: 1, max: 5 }),
450
- incident_id: `inc_${v.uid(8)}`,
451
- original_severity: severity,
452
- original_alert_type: record.alert_type,
453
- auto_escalated: true,
454
- };
455
- }
456
- }
457
-
458
- // H6: COST OVERRUN PATTERN — cost reports with cost_change > 25% record
459
- // the user, then the next infrastructure-scaled event from that user is
460
- // forced to scale_direction = "down".
461
- if (record.event === "cost report generated" && record.cost_change_percent > COST_OVERRUN_THRESHOLD) {
462
- costOverrunUsers.set(record.user_id, true);
463
- }
464
- if (record.event === "infrastructure scaled" && costOverrunUsers.has(record.user_id)) {
465
- record.scale_direction = "down";
466
- costOverrunUsers.delete(record.user_id);
467
- }
468
-
469
- return record;
470
- }
471
-
472
- function handleUserHooks(record) {
473
- // H8: ENTERPRISE VS STARTUP — company size determines seat count, ACV,
474
- // and CSM. Real profile attrs.
475
- const companySize = record.company_size;
476
- if (companySize === "enterprise") {
477
- record.seat_count = chance.integer({ min: 50, max: 500 });
478
- record.annual_contract_value = chance.integer({ min: 50000, max: 500000 });
479
- record.customer_success_manager = true;
480
- } else if (companySize === "mid_market") {
481
- record.seat_count = chance.integer({ min: 10, max: 50 });
482
- record.annual_contract_value = chance.integer({ min: 12000, max: 50000 });
483
- record.customer_success_manager = false;
484
- } else if (companySize === "smb") {
485
- record.seat_count = chance.integer({ min: 3, max: 10 });
486
- record.annual_contract_value = chance.integer({ min: 3600, max: 12000 });
487
- record.customer_success_manager = false;
488
- } else if (companySize === "startup") {
489
- record.seat_count = chance.integer({ min: 1, max: 5 });
490
- record.annual_contract_value = chance.integer({ min: 0, max: 3600 });
491
- record.customer_success_manager = false;
492
- }
493
- record.customer_health_score = chance.integer({ min: 1, max: 100 });
494
- return record;
495
- }
496
-
497
- function handleEverythingHooks(record, meta) {
498
- const datasetStart = dayjs.unix(meta.datasetStart);
499
- const userEvents = record;
500
- const profile = meta.profile;
501
-
502
- userEvents.forEach(e => {
503
- e.plan_tier = profile.plan_tier;
504
- e.cloud_provider = profile.cloud_provider;
505
- });
506
-
507
- // H1a: END-OF-QUARTER SPIKE — days 100-110, billing events flip
508
- // event_type to plan_upgraded 40% of the time.
509
- userEvents.forEach(e => {
510
- if (e.event !== "billing event") return;
511
- const dayInDataset = dayjs(e.time).diff(datasetStart, "days", true);
512
- if (dayInDataset >= EOQ_START_DAY && dayInDataset <= EOQ_END_DAY && chance.bool({ likelihood: EOQ_UPGRADE_LIKELIHOOD })) {
513
- e.event_type = "plan_upgraded";
514
- }
515
- });
516
-
517
- // H1b: END-OF-QUARTER TEAM INVITE SPIKE — days 100-110, clone 50% of
518
- // team-member-invited events (push, not return).
519
- for (let i = userEvents.length - 1; i >= 0; i--) {
520
- const e = userEvents[i];
521
- if (e.event !== "team member invited") continue;
522
- const dayInDataset = dayjs(e.time).diff(datasetStart, "days", true);
523
- if (dayInDataset >= EOQ_START_DAY && dayInDataset <= EOQ_END_DAY && chance.bool({ likelihood: EOQ_INVITE_CLONE_LIKELIHOOD })) {
524
- userEvents.push({
525
- ...e,
526
- time: dayjs(e.time).add(chance.integer({ min: 1, max: 60 }), "minutes").toISOString(),
527
- user_id: e.user_id,
528
- role: chance.pickone(["editor", "viewer"]),
529
- invitation_method: chance.pickone(["email", "sso", "slack"]),
530
- });
531
- }
532
- }
533
-
534
- // H2: CHURNED ACCOUNT SILENCING — ~20% of users (hash %5) have post-day-30
535
- // events removed.
536
- if (userEvents && userEvents.length > 0) {
537
- const firstEvent = userEvents[0];
538
- const idHash = String(firstEvent.user_id || firstEvent.device_id).split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
539
- if ((idHash % CHURN_USER_HASH_MOD) === 0) {
540
- for (let i = userEvents.length - 1; i >= 0; i--) {
541
- const dayInDataset = dayjs(userEvents[i].time).diff(datasetStart, "days", true);
542
- if (dayInDataset > CHURN_CUTOFF_DAYS) {
543
- userEvents.splice(i, 1);
544
- }
545
- }
546
- }
547
- }
548
-
549
- // H4: INTEGRATION USERS SUCCEED — Slack+PagerDuty users get alert
550
- // response_time_mins 0.4x and resolution_time_mins 0.5x.
551
- let hasSlack = false;
552
- let hasPagerduty = false;
553
- userEvents.forEach((event) => {
554
- if (event.event === "integration configured") {
555
- if (event.integration_type === "slack") hasSlack = true;
556
- if (event.integration_type === "pagerduty") hasPagerduty = true;
557
- }
558
- });
559
- if (hasSlack && hasPagerduty) {
560
- userEvents.forEach((event) => {
561
- if (event.event === "alert acknowledged" && event.response_time_mins) {
562
- event.response_time_mins = Math.floor(event.response_time_mins * INTEGRATION_RESPONSE_FACTOR);
563
- }
564
- if (event.event === "alert resolved" && event.resolution_time_mins) {
565
- event.resolution_time_mins = Math.floor(event.resolution_time_mins * INTEGRATION_RESOLUTION_FACTOR);
566
- }
567
- });
568
- }
569
-
570
- // H5 + H10: DOCS MAGIC NUMBER — sweet 4-7 docs → +40% extra cloned
571
- // service-deployed events; over 8+ → drop 25% of service-deployed events.
572
- const docsCount = userEvents.filter(e => e.event === "documentation viewed").length;
573
- const deployTemplate = userEvents.find(e => e.event === "service deployed");
574
- if (docsCount >= DOCS_SWEET_MIN && docsCount <= DOCS_SWEET_MAX && deployTemplate) {
575
- const lastEvent = userEvents[userEvents.length - 1];
576
- const extraDeploys = chance.integer({ min: DOCS_EXTRA_DEPLOYS_MIN, max: DOCS_EXTRA_DEPLOYS_MAX });
577
- for (let i = 0; i < extraDeploys; i++) {
578
- userEvents.push({
579
- ...deployTemplate,
580
- time: dayjs(lastEvent.time).add(chance.integer({ min: 1, max: 48 }), "hours").toISOString(),
581
- user_id: lastEvent.user_id,
582
- service_id: chance.pickone(serviceIds),
583
- service_type: chance.pickone(["web_app", "api", "database", "cache", "queue", "ml_model"]),
584
- environment: "production",
585
- cloud_provider: profile.cloud_provider,
586
- });
587
- }
588
- } else if (docsCount >= DOCS_OVER_THRESHOLD) {
589
- for (let i = userEvents.length - 1; i >= 0; i--) {
590
- if (userEvents[i].event === "service deployed" && chance.bool({ likelihood: DOCS_DEPLOY_DROP_LIKELIHOOD })) {
591
- userEvents.splice(i, 1);
592
- }
593
- }
594
- }
595
-
596
- // H7: FAILED DEPLOYMENT RECOVERY — find failed→success pairs in this
597
- // user's pipeline events, multiply duration_sec by 1.5 on the recovery
598
- // deploy.
599
- const pipelineEvents = userEvents
600
- .filter(e => e.event === "deployment pipeline run")
601
- .sort((a, b) => a.time.localeCompare(b.time));
602
- for (let i = 1; i < pipelineEvents.length; i++) {
603
- if (pipelineEvents[i - 1].status === "failed" && pipelineEvents[i].status === "success") {
604
- pipelineEvents[i].duration_sec = Math.floor((pipelineEvents[i].duration_sec || 300) * FAILED_DEPLOY_RECOVERY_MULT);
605
- }
606
- }
607
-
608
- // H9: INCIDENT RESPONSE TTC — enterprise resolves faster, startup
609
- // resolves slower. Scale response_time_mins on acknowledged events and
610
- // resolution_time_mins on resolved events by company_size, and shift the
611
- // first incident-response funnel sequence timestamps for funnel TTC
612
- // reports. Compounds with H4 (integration users).
613
- const companySegment = profile?.company_size;
614
- const ttcFactor = (
615
- companySegment === "enterprise" ? TTC_ENTERPRISE_FACTOR :
616
- companySegment === "startup" ? TTC_STARTUP_FACTOR :
617
- 1.0
618
- );
619
- if (ttcFactor !== 1.0) {
620
- // Timestamp shift: affects Mixpanel funnel TTC
621
- const incidentSeq = findFirstSequence(
622
- userEvents,
623
- ["alert triggered", "alert acknowledged", "alert resolved"],
624
- INCIDENT_SEQ_WINDOW_MINS
625
- );
626
- if (incidentSeq) scaleFunnelTTC(incidentSeq, ttcFactor);
627
- // Property scale: affects Insights AVG reports
628
- userEvents.forEach(e => {
629
- if (e.event === "alert acknowledged" && e.response_time_mins) {
630
- e.response_time_mins = Math.max(1, Math.round(e.response_time_mins * ttcFactor));
631
- }
632
- if (e.event === "alert resolved" && e.resolution_time_mins) {
633
- e.resolution_time_mins = Math.max(1, Math.round(e.resolution_time_mins * ttcFactor));
634
- }
635
- });
636
- }
637
-
638
- return record;
639
- }
640
-
641
- // ── CONFIG ──
642
- /** @type {Config} */
643
- const config = {
644
- seed: SEED,
645
- datasetStart: DATASET_START,
646
- datasetEnd: DATASET_END,
647
- avgEventsPerUserPerDay: EVENTS_PER_DAY,
648
- numUsers: NUM_USERS,
649
- format: "json",
650
- gzip: true,
651
- credentials: {
652
- token,
653
- },
654
- switches: {
655
- hasSessionIds: true,
656
- alsoInferFunnels: false,
657
- hasLocation: true,
658
- hasAndroidDevices: false,
659
- hasIOSDevices: false,
660
- hasDesktopDevices: true,
661
- hasBrowser: true,
662
- hasCampaigns: false,
663
- isAnonymous: false,
664
- hasAdSpend: false,
665
- hasAvatar: true,
666
- },
667
- // Phase 2 identity model — B2B SaaS reference. Engineers commonly use 1-2
668
- // devices (desktop + work laptop). avgDevicePerUser:2 puts a meaningful
669
- // per-session sticky-device pattern in Mixpanel device dashboards.
670
- identity: {
671
- avgDevicePerUser: 2,
672
- },
673
- concurrency: 1,
674
- writeToDisk: false,
675
- scdProps: {
676
- primary_role: {
677
- values: ["viewer", "editor", "admin", "owner"],
678
- frequency: "month",
679
- timing: "fuzzy",
680
- max: 6
681
- },
682
- plan_tier: {
683
- values: ["starter", "growth", "enterprise", "scale"],
684
- frequency: "month",
685
- timing: "fixed",
686
- max: 6,
687
- type: "company_id"
688
- }
689
- },
690
-
691
- funnels: [
692
- {
693
- // First funnel — `workspace created` is the auth event for B2B users.
694
- // Models real B2B onboarding: most teams take ≤1 retry before sticking.
695
- sequence: ["workspace created", "service deployed", "dashboard viewed"],
696
- isFirstFunnel: true,
697
- conversionRate: 70,
698
- timeToConvert: 2,
699
- attempts: { min: 0, max: 1 },
700
- },
701
- {
702
- // Daily monitoring: dashboards, queries, API calls (most common)
703
- sequence: ["dashboard viewed", "query executed", "api call"],
704
- conversionRate: 80,
705
- timeToConvert: 0.5,
706
- weight: 5,
707
- },
708
- {
709
- // Incident response pipeline
710
- sequence: ["alert triggered", "alert acknowledged", "alert resolved"],
711
- conversionRate: 55,
712
- timeToConvert: 6,
713
- weight: 4,
714
- },
715
- {
716
- // Deployment cycle
717
- sequence: ["deployment pipeline run", "service deployed", "dashboard viewed"],
718
- conversionRate: 65,
719
- timeToConvert: 1,
720
- weight: 3,
721
- experiment: {
722
- name: "Canary Deploys",
723
- variants: [
724
- { name: "Control" },
725
- { name: "Canary Deploys", conversionMultiplier: 1.2, ttcMultiplier: 0.85 },
726
- ],
727
- startDaysBeforeEnd: 45,
728
- },
729
- },
730
- {
731
- // Infrastructure management
732
- sequence: ["cost report generated", "infrastructure scaled", "security scan"],
733
- conversionRate: 50,
734
- timeToConvert: 4,
735
- weight: 2,
736
- },
737
- {
738
- // Team and config management
739
- sequence: ["team member invited", "integration configured", "feature flag toggled"],
740
- conversionRate: 40,
741
- timeToConvert: 8,
742
- weight: 2,
743
- },
744
- {
745
- // Documentation and runbook usage
746
- sequence: ["documentation viewed", "runbook executed", "service deployed"],
747
- conversionRate: 45,
748
- timeToConvert: 2,
749
- weight: 2,
750
- },
751
- {
752
- // Billing and account management
753
- sequence: ["billing event", "dashboard viewed"],
754
- conversionRate: 60,
755
- timeToConvert: 1,
756
- weight: 1,
757
- },
758
- ],
759
-
760
- events: [
761
- {
762
- event: "workspace created",
763
- weight: 1,
764
- isFirstEvent: true,
765
- // Phase 2 identity: workspace creation is the B2B equivalent of Sign Up
766
- // — engine stamps user_id+device_id on this event when it fires inside
767
- // the user's first funnel.
768
- isAuthEvent: true,
769
- properties: {
770
- company_size: ["startup", "smb", "mid_market", "enterprise"],
771
- industry: ["tech", "finance", "healthcare", "retail", "media"],
772
- }
773
- },
774
- {
775
- event: "service deployed",
776
- weight: 10,
777
- isStrictEvent: false,
778
- properties: {
779
- service_id: serviceIds,
780
- service_type: ["web_app", "api", "database", "cache", "queue", "ml_model"],
781
- environment: ["production", "staging", "dev"],
782
- cloud_provider: ["aws", "gcp", "azure"],
783
- }
784
- },
785
- {
786
- event: "dashboard viewed",
787
- weight: 20,
788
- isStrictEvent: false,
789
- properties: {
790
- dashboard_type: ["overview", "cost", "performance", "security", "custom"],
791
- time_range: ["1h", "6h", "24h", "7d", "30d"],
792
- }
793
- },
794
- {
795
- event: "alert triggered",
796
- weight: 12,
797
- isStrictEvent: false,
798
- properties: {
799
- alert_id: alertIds,
800
- severity: ["info", "warning", "critical", "emergency"],
801
- alert_type: ["cpu", "memory", "latency", "error_rate", "disk", "network"],
802
- service_id: serviceIds,
803
- }
804
- },
805
- {
806
- event: "incident created",
807
- weight: 1,
808
- properties: {
809
- escalation_level: ["P1", "P2"],
810
- teams_paged: u.weighNumRange(1, 5),
811
- incident_id: () => `inc_${v.uid(8)}`,
812
- original_severity: ["critical", "emergency"],
813
- original_alert_type: ["cpu", "memory", "latency", "error_rate", "disk", "network"],
814
- service_id: serviceIds,
815
- auto_escalated: [true],
816
- }
817
- },
818
- {
819
- event: "alert acknowledged",
820
- weight: 8,
821
- isStrictEvent: false,
822
- properties: {
823
- alert_id: alertIds,
824
- response_time_mins: u.weighNumRange(1, 120),
825
- acknowledged_by_role: ["engineer", "sre", "manager", "oncall"],
826
- }
827
- },
828
- {
829
- event: "alert resolved",
830
- weight: 7,
831
- isStrictEvent: false,
832
- properties: {
833
- alert_id: alertIds,
834
- resolution_time_mins: u.weighNumRange(5, 1440),
835
- root_cause: ["config_change", "capacity", "bug", "dependency", "network"],
836
- }
837
- },
838
- {
839
- event: "deployment pipeline run",
840
- weight: 9,
841
- isStrictEvent: false,
842
- properties: {
843
- pipeline_id: pipelineIds,
844
- status: ["success", "failed", "cancelled"],
845
- duration_sec: u.weighNumRange(30, 1800),
846
- commit_count: u.weighNumRange(1, 20),
847
- }
848
- },
849
- {
850
- event: "infrastructure scaled",
851
- weight: 5,
852
- isStrictEvent: false,
853
- properties: {
854
- service_id: serviceIds,
855
- scale_direction: ["up", "up", "up", "down"],
856
- previous_capacity: u.weighNumRange(1, 100),
857
- new_capacity: u.weighNumRange(1, 100),
858
- auto_scaled: [false, false, false, false, false, false, true],
859
- }
860
- },
861
- {
862
- event: "cost report generated",
863
- weight: 4,
864
- isStrictEvent: false,
865
- properties: {
866
- report_period: ["daily", "weekly", "monthly"],
867
- total_cost: u.weighNumRange(100, 50000),
868
- cost_change_percent: u.weighNumRange(-30, 50),
869
- }
870
- },
871
- {
872
- event: "team member invited",
873
- weight: 3,
874
- isStrictEvent: false,
875
- properties: {
876
- role: ["admin", "editor", "viewer", "billing"],
877
- invitation_method: ["email", "sso", "slack"],
878
- }
879
- },
880
- {
881
- event: "integration configured",
882
- weight: 4,
883
- isStrictEvent: false,
884
- properties: {
885
- integration_type: ["slack", "pagerduty", "jira", "github", "datadog", "terraform"],
886
- status: ["active", "paused", "error"],
887
- }
888
- },
889
- {
890
- event: "query executed",
891
- weight: 15,
892
- properties: {
893
- query_type: ["metrics", "logs", "traces"],
894
- time_range_hours: u.weighNumRange(1, 720),
895
- result_count: u.weighNumRange(0, 10000),
896
- }
897
- },
898
- {
899
- event: "runbook executed",
900
- weight: 3,
901
- properties: {
902
- runbook_id: runbookIds,
903
- trigger: ["manual", "automated", "alert_triggered"],
904
- success: [false, false, false, false, false, false, true],
905
- }
906
- },
907
- {
908
- event: "billing event",
909
- weight: 3,
910
- isStrictEvent: false,
911
- properties: {
912
- event_type: ["invoice_generated", "invoice_generated", "payment_received", "payment_received", "payment_received", "payment_failed", "plan_upgraded", "plan_downgraded"],
913
- amount: u.weighNumRange(99, 25000),
914
- }
915
- },
916
- {
917
- event: "security scan",
918
- weight: 6,
919
- properties: {
920
- scan_type: ["vulnerability", "compliance", "access_audit"],
921
- findings_count: u.weighNumRange(0, 50),
922
- critical_findings: u.weighNumRange(0, 10),
923
- }
924
- },
925
- {
926
- event: "api call",
927
- weight: 16,
928
- properties: {
929
- endpoint: ["/deploy", "/status", "/metrics", "/alerts", "/config", "/billing"],
930
- method: ["GET", "POST", "PUT", "DELETE"],
931
- response_time_ms: u.weighNumRange(10, 5000),
932
- status_code: [200, 201, 400, 401, 403, 500, 503],
933
- }
934
- },
935
- {
936
- event: "documentation viewed",
937
- weight: 7,
938
- isStrictEvent: false,
939
- properties: {
940
- doc_section: ["getting_started", "api_reference", "best_practices", "troubleshooting", "changelog"],
941
- time_on_page_sec: u.weighNumRange(5, 600),
942
- }
943
- },
944
- {
945
- event: "feature flag toggled",
946
- weight: 4,
947
- properties: {
948
- flag_name: () => `flag_${chance.word()}`,
949
- new_state: ["disabled", "disabled", "disabled", "disabled", "disabled", "disabled", "enabled"],
950
- environment: ["production", "staging", "dev"],
951
- }
952
- },
953
- ],
954
-
955
- superProps: {
956
- plan_tier: ["free", "free", "team", "team", "business", "enterprise"],
957
- cloud_provider: ["aws", "gcp", "azure", "multi_cloud"],
958
- },
959
-
960
- userProps: {
961
- company_size: ["startup", "startup", "smb", "mid_market", "enterprise"],
962
- primary_role: ["engineer", "sre", "devops", "manager", "executive"],
963
- team_name: ["Platform", "Backend", "Frontend", "Data", "Security", "Infrastructure"],
964
- seat_count: [1],
965
- annual_contract_value: [0],
966
- customer_success_manager: [false],
967
- customer_health_score: u.weighNumRange(1, 100),
968
- plan_tier: ["free", "free", "team", "team", "business", "enterprise"],
969
- cloud_provider: ["aws", "gcp", "azure", "multi_cloud"],
970
- },
971
-
972
- groupKeys: [
973
- ["company_id", 300, ["workspace created", "service deployed", "billing event", "team member invited"]],
974
- ],
975
-
976
- groupProps: {
977
- company_id: {
978
- name: () => `${chance.word({ capitalize: true })} ${chance.pickone(["Systems", "Technologies", "Labs", "Cloud", "Digital", "Networks", "Solutions"])}`,
979
- industry: ["tech", "finance", "healthcare", "retail", "media", "manufacturing", "logistics"],
980
- employee_count: ["1-10", "11-50", "51-200", "201-1000", "1001-5000", "5000+"],
981
- arr_bucket: ["<10k", "10k-50k", "50k-200k", "200k-1M", "1M+"],
982
- }
983
- },
984
-
985
- lookupTables: [],
986
-
987
- hook(record, type, meta) {
988
- if (type === "event") return handleEventHooks(record);
989
- if (type === "user") return handleUserHooks(record);
990
- if (type === "everything") return handleEverythingHooks(record, meta);
991
- return record;
992
- }
993
- };
994
-
995
- export default config;