@ak--47/dungeon-master 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dungeons/technical/ad-spend-schema.json +128 -0
- package/dungeons/technical/ad-spend.js +116 -0
- package/dungeons/technical/anonymous-users-schema.json +92 -0
- package/dungeons/technical/anonymous-users.js +102 -0
- package/dungeons/technical/array-of-object-lookup-schema.json +191 -0
- package/dungeons/{array-of-object-lookup.js → technical/array-of-object-lookup.js} +12 -5
- package/dungeons/technical/experiments-schema.json +203 -0
- package/dungeons/technical/experiments.js +168 -0
- package/dungeons/technical/foobar-schema.json +362 -0
- package/dungeons/{foobar.js → technical/foobar.js} +3 -3
- package/dungeons/technical/group-analytics-schema.json +241 -0
- package/dungeons/technical/group-analytics.js +151 -0
- package/dungeons/technical/mirror-strategies-schema.json +84 -0
- package/dungeons/technical/mirror-strategies.js +126 -0
- package/dungeons/technical/nested-objects-schema.json +145 -0
- package/dungeons/technical/nested-objects.js +207 -0
- package/dungeons/technical/retention-cadence-schema.json +37 -0
- package/dungeons/{retention-cadence.js → technical/retention-cadence.js} +2 -3
- package/dungeons/technical/sanity-schema.json +185 -0
- package/dungeons/{sanity.js → technical/sanity.js} +4 -2
- package/dungeons/technical/scale-test-schema.json +70 -0
- package/dungeons/technical/scale-test.js +90 -0
- package/dungeons/technical/scd-schema.json +467 -0
- package/dungeons/{scd.js → technical/scd.js} +20 -10
- package/dungeons/technical/simple-schema.json +362 -0
- package/dungeons/{simple.js → technical/simple.js} +7 -7
- package/dungeons/technical/simplest-schema.json +724 -0
- package/dungeons/{simplest.js → technical/simplest.js} +7 -7
- package/dungeons/technical/text-generation-schema.json +1062 -0
- package/dungeons/{text-generation.js → technical/text-generation.js} +24 -11
- package/dungeons/vertical/community-schema.json +579 -0
- package/dungeons/vertical/community.js +794 -0
- package/dungeons/vertical/devtools-schema.json +601 -0
- package/dungeons/vertical/devtools.js +843 -0
- package/dungeons/vertical/ecommerce-schema.json +604 -0
- package/dungeons/{ecommerce.js → vertical/ecommerce.js} +43 -5
- package/dungeons/vertical/education-schema.json +5686 -0
- package/dungeons/{education.js → vertical/education.js} +70 -39
- package/dungeons/vertical/fintech-schema.json +630 -0
- package/dungeons/{fintech.js → vertical/fintech.js} +107 -56
- package/dungeons/vertical/fitness-schema.json +530 -0
- package/dungeons/vertical/fitness.js +808 -0
- package/dungeons/vertical/food-delivery-schema.json +36728 -0
- package/dungeons/{food.js → vertical/food-delivery.js} +67 -26
- package/dungeons/vertical/gaming-schema.json +438 -0
- package/dungeons/{gaming.js → vertical/gaming.js} +29 -16
- package/dungeons/vertical/healthcare-schema.json +549 -0
- package/dungeons/vertical/healthcare.js +775 -0
- package/dungeons/vertical/insurance-application-schema.json +485 -0
- package/dungeons/{insurance-application.js → vertical/insurance-application.js} +41 -31
- package/dungeons/vertical/logistics-schema.json +574 -0
- package/dungeons/vertical/logistics.js +799 -0
- package/dungeons/vertical/marketplace-schema.json +533 -0
- package/dungeons/vertical/marketplace.js +850 -0
- package/dungeons/vertical/media-schema.json +4749 -0
- package/dungeons/{media.js → vertical/media.js} +103 -19
- package/dungeons/vertical/rpg-schema.json +2491 -0
- package/dungeons/{rpg.js → vertical/rpg.js} +87 -60
- package/dungeons/vertical/sass-schema.json +3128 -0
- package/dungeons/{sass.js → vertical/sass.js} +65 -26
- package/dungeons/vertical/social-schema.json +620 -0
- package/dungeons/{social.js → vertical/social.js} +76 -17
- package/dungeons/vertical/travel-schema.json +580 -0
- package/dungeons/vertical/travel.js +742 -0
- package/index.js +20 -16
- package/lib/core/config-validator.js +286 -1
- package/lib/core/context.js +16 -5
- package/lib/core/storage.js +2 -0
- package/lib/generators/adspend.js +3 -3
- package/lib/generators/events.js +141 -14
- package/lib/generators/funnels.js +67 -7
- package/lib/generators/text.js +2 -2
- package/lib/orchestrators/user-loop.js +444 -22
- package/lib/templates/soup-presets.js +2 -2
- package/lib/utils/utils.js +4 -5
- package/package.json +4 -3
- package/scripts/dungeon-to-json.mjs +163 -0
- package/scripts/extract-dungeon-schema.mjs +343 -0
- package/scripts/json-to-dungeon.mjs +200 -0
- package/scripts/run-dungeon.mjs +76 -0
- package/scripts/run-many.mjs +118 -0
- package/scripts/verify-runner.mjs +42 -0
- package/types.d.ts +316 -1
- package/dungeons/array-of-object-lookup-schema.json +0 -327
- package/dungeons/ecommerce-schema.json +0 -462
- package/dungeons/education-schema.json +0 -2409
- package/dungeons/fintech-schema.json +0 -14034
- package/dungeons/foobar-schema.json +0 -403
- package/dungeons/food-delivery-schema.json +0 -192
- package/dungeons/food-delivery.js +0 -602
- package/dungeons/food-schema.json +0 -1152
- package/dungeons/gaming-schema.json +0 -1270
- package/dungeons/insurance-application-schema.json +0 -204
- package/dungeons/media-schema.json +0 -906
- package/dungeons/retention-cadence-schema.json +0 -78
- package/dungeons/rpg-schema.json +0 -4526
- package/dungeons/sanity-schema.json +0 -255
- package/dungeons/sass-schema.json +0 -1291
- package/dungeons/scd-schema.json +0 -919
- package/dungeons/simple-schema.json +0 -608
- package/dungeons/simplest-schema.json +0 -1418
- package/dungeons/social-schema.json +0 -1118
- package/dungeons/text-generation-schema.json +0 -3096
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import utc from "dayjs/plugin/utc.js";
|
|
3
|
+
import "dotenv/config";
|
|
4
|
+
import * as u from "../../lib/utils/utils.js";
|
|
5
|
+
import * as v from "ak-tools";
|
|
6
|
+
|
|
7
|
+
const SEED = "dm4-devtools";
|
|
8
|
+
dayjs.extend(utc);
|
|
9
|
+
const chance = u.initChance(SEED);
|
|
10
|
+
const num_users = 5_000;
|
|
11
|
+
const days = 100;
|
|
12
|
+
const NOW = dayjs();
|
|
13
|
+
const DATASET_START = NOW.subtract(days, "days");
|
|
14
|
+
|
|
15
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
16
|
+
|
|
17
|
+
// Generate consistent pipeline/repo IDs at module level
|
|
18
|
+
const pipelineIds = v.range(1, 80).map(() => `PIPE_${v.uid(6)}`);
|
|
19
|
+
const repoIds = v.range(1, 150).map(() => `REPO_${v.uid(6)}`);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* ===============================================================
|
|
23
|
+
* DATASET OVERVIEW
|
|
24
|
+
* ===============================================================
|
|
25
|
+
*
|
|
26
|
+
* CodeForge -- a developer platform for builds, deploys, monitoring,
|
|
27
|
+
* code review, and team collaboration. Think GitHub + Vercel + PagerDuty
|
|
28
|
+
* in a unified CI/CD experience.
|
|
29
|
+
*
|
|
30
|
+
* - 5,000 users over 100 days, ~600K events
|
|
31
|
+
* - Multi-role system: platform engineers (15%), full-stack devs (35%),
|
|
32
|
+
* junior devs (30%), devops leads (10%), open source users (10%)
|
|
33
|
+
* - Core loop: connect repo -> configure pipeline -> build -> deploy -> monitor
|
|
34
|
+
* - Revenue: free / team ($25, 14-day trial) / business ($75) / enterprise ($200)
|
|
35
|
+
*
|
|
36
|
+
* Advanced Features:
|
|
37
|
+
* - Personas: 5 archetypes with distinct activity, conversion, and churn profiles
|
|
38
|
+
* - World Events: major outage (day 42), conference launch (day 60)
|
|
39
|
+
* - Data Quality: late arrivals, duplicates, bot pollution
|
|
40
|
+
* - Subscription: 4-tier revenue lifecycle with trial conversion
|
|
41
|
+
* - Geo: US/EU/India/rest with timezone-aware activity
|
|
42
|
+
* - Features: copilot_integration (day 30), deployment_preview (day 50)
|
|
43
|
+
* - Anomalies: extreme build durations, alert burst during outage
|
|
44
|
+
*
|
|
45
|
+
* Key entities:
|
|
46
|
+
* - pipeline_id: CI/CD pipeline for a repo
|
|
47
|
+
* - repo_id: source code repository
|
|
48
|
+
* - build_status / deploy_status: success/failed/cancelled
|
|
49
|
+
* - ai_assist: manual vs copilot (driven by Feature rollout)
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* ===============================================================
|
|
54
|
+
* ANALYTICS HOOKS (8 hooks)
|
|
55
|
+
* ===============================================================
|
|
56
|
+
*
|
|
57
|
+
* ---------------------------------------------------------------
|
|
58
|
+
* 1. BUILD FAILURE CASCADE (event hook)
|
|
59
|
+
* ---------------------------------------------------------------
|
|
60
|
+
*
|
|
61
|
+
* PATTERN: Builds with status "failed" get 2x build_duration_sec.
|
|
62
|
+
* Failed builds take longer because the full test suite runs before
|
|
63
|
+
* failing, and retries compound the duration.
|
|
64
|
+
*
|
|
65
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
66
|
+
*
|
|
67
|
+
* Report 1: Build Duration by Status
|
|
68
|
+
* - Report type: Insights
|
|
69
|
+
* - Event: "build completed"
|
|
70
|
+
* - Measure: Average of "build_duration_sec"
|
|
71
|
+
* - Breakdown: "build_status"
|
|
72
|
+
* - Expected: failed ~2x longer than success
|
|
73
|
+
* (failed ~ 480s, success ~ 240s)
|
|
74
|
+
*
|
|
75
|
+
* REAL-WORLD ANALOGUE: Failed CI builds run full test suites,
|
|
76
|
+
* timeout waiting for flaky tests, and trigger retry cascades
|
|
77
|
+
* that inflate overall build times.
|
|
78
|
+
*
|
|
79
|
+
* ---------------------------------------------------------------
|
|
80
|
+
* 2. NIGHT DEPLOY RISK (event hook)
|
|
81
|
+
* ---------------------------------------------------------------
|
|
82
|
+
*
|
|
83
|
+
* PATTERN: Deployments between 10PM-6AM get status forced to
|
|
84
|
+
* "failed" 40% of the time. Night deploys are risky because
|
|
85
|
+
* fewer engineers are online to catch issues.
|
|
86
|
+
*
|
|
87
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
88
|
+
*
|
|
89
|
+
* Report 1: Deploy Failure Rate by Hour
|
|
90
|
+
* - Report type: Insights
|
|
91
|
+
* - Event: "deployment completed"
|
|
92
|
+
* - Measure: Total
|
|
93
|
+
* - Filter: deploy_status = "failed"
|
|
94
|
+
* - Breakdown: "is_night_deploy"
|
|
95
|
+
* - Expected: is_night_deploy=true shows ~40% failure vs ~15% baseline
|
|
96
|
+
*
|
|
97
|
+
* Report 2: Night Deploy Volume
|
|
98
|
+
* - Report type: Insights
|
|
99
|
+
* - Event: "deployment completed"
|
|
100
|
+
* - Measure: Total
|
|
101
|
+
* - Breakdown: "is_night_deploy"
|
|
102
|
+
* - Expected: ~15-20% of deploys happen at night
|
|
103
|
+
*
|
|
104
|
+
* REAL-WORLD ANALOGUE: Night and weekend deploys have higher
|
|
105
|
+
* failure rates due to skeleton crews and delayed incident response.
|
|
106
|
+
*
|
|
107
|
+
* ---------------------------------------------------------------
|
|
108
|
+
* 3. COPILOT ADOPTION -> PR VELOCITY (everything hook)
|
|
109
|
+
* ---------------------------------------------------------------
|
|
110
|
+
*
|
|
111
|
+
* PATTERN: Users with ai_assist="copilot" on any PR event get
|
|
112
|
+
* 1.5x more pull_request_created events cloned into their stream.
|
|
113
|
+
* AI-assisted developers ship more PRs.
|
|
114
|
+
*
|
|
115
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
116
|
+
*
|
|
117
|
+
* Report 1: PR Volume by AI Assist Mode
|
|
118
|
+
* - Report type: Insights
|
|
119
|
+
* - Event: "pull request created"
|
|
120
|
+
* - Measure: Total per user (average)
|
|
121
|
+
* - Breakdown: "ai_assist"
|
|
122
|
+
* - Expected: copilot users ~1.5x more PRs than manual users
|
|
123
|
+
*
|
|
124
|
+
* REAL-WORLD ANALOGUE: AI coding assistants measurably increase
|
|
125
|
+
* developer throughput, particularly for boilerplate and tests.
|
|
126
|
+
*
|
|
127
|
+
* ---------------------------------------------------------------
|
|
128
|
+
* 4. ON-CALL ROTATION FATIGUE (everything hook)
|
|
129
|
+
* ---------------------------------------------------------------
|
|
130
|
+
*
|
|
131
|
+
* PATTERN: Users with >20 alert_triggered events get increasing
|
|
132
|
+
* response_time_minutes on incident events. Alert fatigue causes
|
|
133
|
+
* slower response as on-call rotations grind engineers down.
|
|
134
|
+
*
|
|
135
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
136
|
+
*
|
|
137
|
+
* Report 1: Response Time vs Alert Volume
|
|
138
|
+
* - Report type: Insights
|
|
139
|
+
* - Event: "incident resolved"
|
|
140
|
+
* - Measure: Average of "response_time_minutes"
|
|
141
|
+
* - Filter: users with high alert counts
|
|
142
|
+
* - Expected: heavy on-call users show 2-3x response time
|
|
143
|
+
* (fatigued ~ 90min, normal ~ 30min)
|
|
144
|
+
*
|
|
145
|
+
* REAL-WORLD ANALOGUE: On-call burnout is a top cause of attrition
|
|
146
|
+
* in SRE/DevOps. Alert fatigue degrades response quality over time.
|
|
147
|
+
*
|
|
148
|
+
* ---------------------------------------------------------------
|
|
149
|
+
* 5. OPEN SOURCE -> PAID CONVERSION (everything hook)
|
|
150
|
+
* ---------------------------------------------------------------
|
|
151
|
+
*
|
|
152
|
+
* PATTERN: OSS users with >15 total events get 30% of their events
|
|
153
|
+
* converted to team-tier behavior (subscription_tier set to "team").
|
|
154
|
+
* Power OSS users eventually hit limits and convert.
|
|
155
|
+
*
|
|
156
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
157
|
+
*
|
|
158
|
+
* Report 1: Tier Migration for OSS Users
|
|
159
|
+
* - Report type: Insights
|
|
160
|
+
* - Event: any event
|
|
161
|
+
* - Measure: Total
|
|
162
|
+
* - Filter: segment = "oss_user"
|
|
163
|
+
* - Breakdown: "subscription_tier"
|
|
164
|
+
* - Expected: ~30% of active OSS user events show "team" tier
|
|
165
|
+
*
|
|
166
|
+
* REAL-WORLD ANALOGUE: Open source users who exceed free-tier
|
|
167
|
+
* limits (build minutes, private repos) organically convert to
|
|
168
|
+
* paid plans.
|
|
169
|
+
*
|
|
170
|
+
* ---------------------------------------------------------------
|
|
171
|
+
* 6. POST-OUTAGE RECOVERY (everything hook)
|
|
172
|
+
* ---------------------------------------------------------------
|
|
173
|
+
*
|
|
174
|
+
* PATTERN: After the major outage ends (day 42.25), deployment
|
|
175
|
+
* events get a frequency boost -- extra cloned deployment events
|
|
176
|
+
* represent the flurry of hotfixes and rollback-then-redeploy cycles.
|
|
177
|
+
*
|
|
178
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
179
|
+
*
|
|
180
|
+
* Report 1: Deployment Spike Post-Outage
|
|
181
|
+
* - Report type: Insights
|
|
182
|
+
* - Event: "deployment completed"
|
|
183
|
+
* - Measure: Total
|
|
184
|
+
* - Line chart by day
|
|
185
|
+
* - Expected: visible spike in deploy volume days 43-46
|
|
186
|
+
*
|
|
187
|
+
* REAL-WORLD ANALOGUE: After a major outage, teams push a burst
|
|
188
|
+
* of hotfixes, rollbacks, and emergency deploys to stabilize.
|
|
189
|
+
*
|
|
190
|
+
* ---------------------------------------------------------------
|
|
191
|
+
* 7. DEVOPS LEAD PROFILE ENRICHMENT (user hook)
|
|
192
|
+
* ---------------------------------------------------------------
|
|
193
|
+
*
|
|
194
|
+
* PATTERN: Users in the "devops" segment get team_size boosted
|
|
195
|
+
* to 10-50 and repos_connected boosted to 5-20. DevOps leads
|
|
196
|
+
* manage larger teams and more infrastructure.
|
|
197
|
+
*
|
|
198
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
199
|
+
*
|
|
200
|
+
* Report 1: Team Size by Segment
|
|
201
|
+
* - Report type: Insights
|
|
202
|
+
* - Event: any event
|
|
203
|
+
* - Measure: Average of user property "team_size"
|
|
204
|
+
* - Breakdown: user property "segment"
|
|
205
|
+
* - Expected: devops ~ 30 avg, others ~ 10 avg
|
|
206
|
+
*
|
|
207
|
+
* REAL-WORLD ANALOGUE: DevOps leads oversee platform teams and
|
|
208
|
+
* manage organization-wide CI/CD infrastructure.
|
|
209
|
+
*
|
|
210
|
+
* ---------------------------------------------------------------
|
|
211
|
+
* 8. ENTERPRISE BUILD-DEPLOY FUNNEL LIFT (funnel-pre hook)
|
|
212
|
+
* ---------------------------------------------------------------
|
|
213
|
+
*
|
|
214
|
+
* PATTERN: Users with subscription_tier "enterprise" get 1.5x
|
|
215
|
+
* conversion rate on the build-deploy pipeline funnel. Enterprise
|
|
216
|
+
* customers have dedicated support and optimized pipelines.
|
|
217
|
+
*
|
|
218
|
+
* HOW TO FIND IT IN MIXPANEL:
|
|
219
|
+
*
|
|
220
|
+
* Report 1: Build-Deploy Conversion by Tier
|
|
221
|
+
* - Report type: Funnels
|
|
222
|
+
* - Steps: "build completed" -> "deployment completed" -> "monitoring dashboard viewed"
|
|
223
|
+
* - Breakdown: "subscription_tier" (superProp)
|
|
224
|
+
* - Expected: enterprise ~ 60% vs free ~ 40% conversion
|
|
225
|
+
*
|
|
226
|
+
* REAL-WORLD ANALOGUE: Enterprise CI/CD customers get priority
|
|
227
|
+
* runners, dedicated support, and SLA-backed uptime guarantees
|
|
228
|
+
* that dramatically improve pipeline reliability.
|
|
229
|
+
*
|
|
230
|
+
* ===============================================================
|
|
231
|
+
* EXPECTED METRICS SUMMARY
|
|
232
|
+
* ===============================================================
|
|
233
|
+
*
|
|
234
|
+
* Hook | Metric | Baseline | Effect | Ratio
|
|
235
|
+
* ----------------------------|---------------------|----------|---------|------
|
|
236
|
+
* Build Failure Cascade | build_duration_sec | 240s | 480s | 2x
|
|
237
|
+
* Night Deploy Risk | deploy failure rate | 15% | 40% | 2.7x
|
|
238
|
+
* Copilot PR Velocity | PRs/user | 3 | 4.5 | 1.5x
|
|
239
|
+
* On-Call Fatigue | response_time_min | 30min | 90min | 3x
|
|
240
|
+
* OSS -> Paid Conversion | team tier events | 0% | 30% | -
|
|
241
|
+
* Post-Outage Recovery | deploys/day | 50 | 100+ | 2x+
|
|
242
|
+
* DevOps Lead Profiles | team_size | 10 | 30 | 3x
|
|
243
|
+
* Enterprise Funnel Lift | funnel conversion | 40% | 60% | 1.5x
|
|
244
|
+
*/
|
|
245
|
+
|
|
246
|
+
/** @type {Config} */
|
|
247
|
+
const config = {
|
|
248
|
+
token: "",
|
|
249
|
+
seed: SEED,
|
|
250
|
+
numDays: days,
|
|
251
|
+
numEvents: num_users * 120,
|
|
252
|
+
numUsers: num_users,
|
|
253
|
+
hasAnonIds: false,
|
|
254
|
+
hasSessionIds: true,
|
|
255
|
+
format: "json",
|
|
256
|
+
gzip: true,
|
|
257
|
+
alsoInferFunnels: false,
|
|
258
|
+
hasLocation: true,
|
|
259
|
+
hasAndroidDevices: false,
|
|
260
|
+
hasIOSDevices: false,
|
|
261
|
+
hasDesktopDevices: true,
|
|
262
|
+
hasBrowser: true,
|
|
263
|
+
hasCampaigns: false,
|
|
264
|
+
isAnonymous: false,
|
|
265
|
+
hasAdSpend: false,
|
|
266
|
+
percentUsersBornInDataset: 35,
|
|
267
|
+
hasAvatar: true,
|
|
268
|
+
concurrency: 1,
|
|
269
|
+
writeToDisk: false,
|
|
270
|
+
scdProps: {
|
|
271
|
+
subscription_tier: {
|
|
272
|
+
values: ["free", "pro", "enterprise"],
|
|
273
|
+
frequency: "month",
|
|
274
|
+
timing: "fuzzy",
|
|
275
|
+
max: 6
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
mirrorProps: {},
|
|
279
|
+
lookupTables: [],
|
|
280
|
+
|
|
281
|
+
// -- Events (18) ------------------------------------------
|
|
282
|
+
events: [
|
|
283
|
+
{
|
|
284
|
+
event: "account created",
|
|
285
|
+
weight: 1,
|
|
286
|
+
isFirstEvent: true,
|
|
287
|
+
properties: {
|
|
288
|
+
referral_source: ["organic", "github", "conference", "blog_post", "colleague", "search"],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
event: "build completed",
|
|
293
|
+
weight: 8,
|
|
294
|
+
properties: {
|
|
295
|
+
pipeline_id: chance.pickone.bind(chance, pipelineIds),
|
|
296
|
+
repo_id: chance.pickone.bind(chance, repoIds),
|
|
297
|
+
build_status: ["success", "success", "success", "success", "failed", "cancelled"],
|
|
298
|
+
build_duration_sec: u.weighNumRange(10, 600, 0.4, 240),
|
|
299
|
+
branch: ["main", "main", "develop", "feature", "feature", "hotfix"],
|
|
300
|
+
test_count: u.weighNumRange(10, 500, 0.5, 100),
|
|
301
|
+
test_pass_rate: u.weighNumRange(70, 100, 0.8, 95),
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
event: "deployment completed",
|
|
306
|
+
weight: 5,
|
|
307
|
+
properties: {
|
|
308
|
+
pipeline_id: chance.pickone.bind(chance, pipelineIds),
|
|
309
|
+
repo_id: chance.pickone.bind(chance, repoIds),
|
|
310
|
+
deploy_status: ["success", "success", "success", "failed", "rolled_back"],
|
|
311
|
+
environment: ["production", "production", "staging", "staging", "dev", "preview"],
|
|
312
|
+
deploy_duration_sec: u.weighNumRange(15, 300, 0.4, 90),
|
|
313
|
+
is_night_deploy: [false],
|
|
314
|
+
preview_enabled: [false],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
event: "pull request created",
|
|
319
|
+
weight: 6,
|
|
320
|
+
properties: {
|
|
321
|
+
repo_id: chance.pickone.bind(chance, repoIds),
|
|
322
|
+
pr_size: ["small", "small", "medium", "medium", "large", "xlarge"],
|
|
323
|
+
files_changed: u.weighNumRange(1, 50, 0.4, 8),
|
|
324
|
+
lines_added: u.weighNumRange(1, 2000, 0.3, 150),
|
|
325
|
+
lines_removed: u.weighNumRange(0, 500, 0.3, 40),
|
|
326
|
+
ai_assist: ["manual"],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
event: "code review completed",
|
|
331
|
+
weight: 5,
|
|
332
|
+
properties: {
|
|
333
|
+
repo_id: chance.pickone.bind(chance, repoIds),
|
|
334
|
+
review_result: ["approved", "approved", "approved", "changes_requested", "commented"],
|
|
335
|
+
review_duration_hours: u.weighNumRange(0.5, 72, 0.3, 8),
|
|
336
|
+
comments_count: u.weighNumRange(0, 20, 0.5, 3),
|
|
337
|
+
ai_assist: ["manual"],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
event: "alert triggered",
|
|
342
|
+
weight: 4,
|
|
343
|
+
properties: {
|
|
344
|
+
alert_type: ["error_rate", "latency", "cpu", "memory", "disk", "custom_metric"],
|
|
345
|
+
severity: ["info", "warning", "warning", "critical", "critical"],
|
|
346
|
+
service: ["api", "web", "worker", "database", "cdn", "auth"],
|
|
347
|
+
acknowledged: [true, true, true, false],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
event: "incident created",
|
|
352
|
+
weight: 2,
|
|
353
|
+
properties: {
|
|
354
|
+
severity: ["sev1", "sev2", "sev2", "sev3", "sev3", "sev3"],
|
|
355
|
+
service: ["api", "web", "worker", "database", "cdn", "auth"],
|
|
356
|
+
response_time_minutes: u.weighNumRange(1, 120, 0.3, 30),
|
|
357
|
+
root_cause: ["deploy", "config_change", "dependency", "traffic_spike", "hardware", "unknown"],
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
event: "incident resolved",
|
|
362
|
+
weight: 2,
|
|
363
|
+
properties: {
|
|
364
|
+
severity: ["sev1", "sev2", "sev2", "sev3", "sev3", "sev3"],
|
|
365
|
+
resolution_time_minutes: u.weighNumRange(5, 480, 0.3, 60),
|
|
366
|
+
response_time_minutes: u.weighNumRange(1, 120, 0.3, 30),
|
|
367
|
+
resolution_type: ["hotfix", "rollback", "config_change", "scaling", "restart", "manual"],
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
event: "repository connected",
|
|
372
|
+
weight: 2,
|
|
373
|
+
properties: {
|
|
374
|
+
provider: ["github", "github", "github", "gitlab", "bitbucket"],
|
|
375
|
+
repo_visibility: ["private", "private", "private", "public"],
|
|
376
|
+
language: ["javascript", "python", "go", "rust", "java", "typescript"],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
event: "pipeline configured",
|
|
381
|
+
weight: 2,
|
|
382
|
+
properties: {
|
|
383
|
+
pipeline_id: chance.pickone.bind(chance, pipelineIds),
|
|
384
|
+
pipeline_type: ["build_test", "build_test_deploy", "deploy_only", "lint_test"],
|
|
385
|
+
trigger: ["push", "push", "pull_request", "schedule", "manual"],
|
|
386
|
+
runtime: ["docker", "docker", "node", "python", "go"],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
event: "collaboration invited",
|
|
391
|
+
weight: 2,
|
|
392
|
+
properties: {
|
|
393
|
+
invite_role: ["developer", "developer", "admin", "viewer"],
|
|
394
|
+
invite_method: ["email", "email", "link", "github_team"],
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
event: "monitoring dashboard viewed",
|
|
399
|
+
weight: 5,
|
|
400
|
+
properties: {
|
|
401
|
+
dashboard_type: ["overview", "performance", "errors", "deploys", "custom"],
|
|
402
|
+
time_range: ["1h", "6h", "24h", "7d", "30d"],
|
|
403
|
+
widgets_count: u.weighNumRange(1, 12, 0.5, 4),
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
event: "log searched",
|
|
408
|
+
weight: 4,
|
|
409
|
+
properties: {
|
|
410
|
+
query_type: ["full_text", "structured", "regex"],
|
|
411
|
+
time_range: ["15m", "1h", "6h", "24h", "7d"],
|
|
412
|
+
results_count: u.weighNumRange(0, 500, 0.3, 50),
|
|
413
|
+
service: ["api", "web", "worker", "database", "auth"],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
event: "notification received",
|
|
418
|
+
weight: 6,
|
|
419
|
+
properties: {
|
|
420
|
+
notification_type: ["build_failed", "deploy_completed", "pr_review_requested", "alert_fired", "mention", "billing"],
|
|
421
|
+
channel: ["in_app", "in_app", "email", "slack", "webhook"],
|
|
422
|
+
opened: [true, true, true, false],
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
event: "billing updated",
|
|
427
|
+
weight: 1,
|
|
428
|
+
properties: {
|
|
429
|
+
change_type: ["plan_upgrade", "plan_downgrade", "payment_method", "add_seats", "remove_seats"],
|
|
430
|
+
payment_method: ["credit_card", "credit_card", "invoice", "paypal"],
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
event: "app session",
|
|
435
|
+
weight: 8,
|
|
436
|
+
properties: {
|
|
437
|
+
session_duration_sec: u.weighNumRange(10, 3600, 0.4, 180),
|
|
438
|
+
pages_viewed: u.weighNumRange(1, 20, 0.5, 4),
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
event: "environment created",
|
|
443
|
+
weight: 2,
|
|
444
|
+
properties: {
|
|
445
|
+
env_type: ["production", "staging", "dev", "preview", "test"],
|
|
446
|
+
cloud_provider: ["aws", "aws", "gcp", "azure", "self_hosted"],
|
|
447
|
+
region: ["us-east-1", "us-west-2", "eu-west-1", "ap-south-1"],
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
event: "account deactivated",
|
|
452
|
+
weight: 1,
|
|
453
|
+
isChurnEvent: true,
|
|
454
|
+
returnLikelihood: 0.15,
|
|
455
|
+
isStrictEvent: true,
|
|
456
|
+
properties: {
|
|
457
|
+
reason: ["switched_provider", "cost", "no_longer_needed", "poor_experience", "team_dissolved", "acquired"],
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
|
|
462
|
+
// -- Funnels (5) ------------------------------------------
|
|
463
|
+
funnels: [
|
|
464
|
+
{
|
|
465
|
+
name: "Onboarding",
|
|
466
|
+
sequence: ["account created", "repository connected", "pipeline configured", "build completed"],
|
|
467
|
+
conversionRate: 45,
|
|
468
|
+
order: "sequential",
|
|
469
|
+
isFirstFunnel: true,
|
|
470
|
+
timeToConvert: 72,
|
|
471
|
+
weight: 3,
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: "Build-Deploy Pipeline",
|
|
475
|
+
sequence: ["build completed", "deployment completed", "monitoring dashboard viewed"],
|
|
476
|
+
conversionRate: 40,
|
|
477
|
+
order: "sequential",
|
|
478
|
+
timeToConvert: 48,
|
|
479
|
+
weight: 5,
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: "PR Review Flow",
|
|
483
|
+
sequence: ["pull request created", "code review completed", "build completed", "deployment completed"],
|
|
484
|
+
conversionRate: 35,
|
|
485
|
+
order: "sequential",
|
|
486
|
+
timeToConvert: 72,
|
|
487
|
+
weight: 4,
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
name: "Incident Response",
|
|
491
|
+
sequence: ["alert triggered", "incident created", "incident resolved"],
|
|
492
|
+
conversionRate: 50,
|
|
493
|
+
order: "sequential",
|
|
494
|
+
timeToConvert: 24,
|
|
495
|
+
weight: 3,
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
name: "Upgrade Path",
|
|
499
|
+
sequence: ["app session", "billing updated", "collaboration invited"],
|
|
500
|
+
conversionRate: 20,
|
|
501
|
+
order: "sequential",
|
|
502
|
+
timeToConvert: 168,
|
|
503
|
+
weight: 2,
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
|
|
507
|
+
// -- SuperProps --------------------------------------------
|
|
508
|
+
superProps: {
|
|
509
|
+
subscription_tier: ["free", "free", "free", "team", "team", "business", "enterprise"],
|
|
510
|
+
platform: ["web", "web", "desktop_app", "cli"],
|
|
511
|
+
language: ["javascript", "python", "go", "rust", "java", "typescript"],
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
// -- UserProps ---------------------------------------------
|
|
515
|
+
userProps: {
|
|
516
|
+
dev_role: ["full_stack"],
|
|
517
|
+
segment: ["full_stack"],
|
|
518
|
+
team_size: u.weighNumRange(1, 50, 0.4, 5),
|
|
519
|
+
repos_connected: [0],
|
|
520
|
+
org_name: ["personal"],
|
|
521
|
+
experience_level: ["junior", "junior", "mid", "mid", "mid", "senior", "senior"],
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
// -- Phase 2: Personas ------------------------------------
|
|
525
|
+
personas: [
|
|
526
|
+
{
|
|
527
|
+
name: "platform_engineer",
|
|
528
|
+
weight: 15,
|
|
529
|
+
eventMultiplier: 4.0,
|
|
530
|
+
conversionModifier: 1.5,
|
|
531
|
+
churnRate: 0.01,
|
|
532
|
+
properties: {
|
|
533
|
+
dev_role: "platform_engineer",
|
|
534
|
+
segment: "platform_eng",
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
name: "full_stack_dev",
|
|
539
|
+
weight: 35,
|
|
540
|
+
eventMultiplier: 1.5,
|
|
541
|
+
conversionModifier: 1.0,
|
|
542
|
+
churnRate: 0.05,
|
|
543
|
+
properties: {
|
|
544
|
+
dev_role: "full_stack",
|
|
545
|
+
segment: "full_stack",
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: "junior_dev",
|
|
550
|
+
weight: 30,
|
|
551
|
+
eventMultiplier: 0.8,
|
|
552
|
+
conversionModifier: 0.6,
|
|
553
|
+
churnRate: 0.12,
|
|
554
|
+
properties: {
|
|
555
|
+
dev_role: "junior",
|
|
556
|
+
segment: "junior",
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
name: "devops_lead",
|
|
561
|
+
weight: 10,
|
|
562
|
+
eventMultiplier: 2.0,
|
|
563
|
+
conversionModifier: 1.3,
|
|
564
|
+
churnRate: 0.02,
|
|
565
|
+
properties: {
|
|
566
|
+
dev_role: "devops_lead",
|
|
567
|
+
segment: "devops",
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
name: "open_source_user",
|
|
572
|
+
weight: 10,
|
|
573
|
+
eventMultiplier: 0.5,
|
|
574
|
+
conversionModifier: 0.3,
|
|
575
|
+
churnRate: 0.15,
|
|
576
|
+
properties: {
|
|
577
|
+
dev_role: "open_source",
|
|
578
|
+
segment: "oss_user",
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
],
|
|
582
|
+
|
|
583
|
+
// -- Phase 2: World Events --------------------------------
|
|
584
|
+
worldEvents: [
|
|
585
|
+
{
|
|
586
|
+
name: "major_outage",
|
|
587
|
+
startDay: 42,
|
|
588
|
+
duration: 0.25,
|
|
589
|
+
volumeMultiplier: 0.05,
|
|
590
|
+
affectsEvents: ["deployment completed", "build completed"],
|
|
591
|
+
injectProps: { outage_window: true },
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
name: "conference_launch",
|
|
595
|
+
startDay: 60,
|
|
596
|
+
duration: 3,
|
|
597
|
+
volumeMultiplier: 2.0,
|
|
598
|
+
affectsEvents: ["account created"],
|
|
599
|
+
injectProps: { promo: "devcon2024" },
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
|
|
603
|
+
// -- Phase 2: Data Quality --------------------------------
|
|
604
|
+
dataQuality: {
|
|
605
|
+
lateArrivingRate: 0.01,
|
|
606
|
+
duplicateRate: 0.005,
|
|
607
|
+
botUsers: 2,
|
|
608
|
+
botEventsPerUser: 300,
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
// -- Phase 2: Subscription --------------------------------
|
|
612
|
+
subscription: {
|
|
613
|
+
plans: [
|
|
614
|
+
{ name: "free", price: 0, default: true },
|
|
615
|
+
{ name: "team", price: 25, trialDays: 14 },
|
|
616
|
+
{ name: "business", price: 75 },
|
|
617
|
+
{ name: "enterprise", price: 200 },
|
|
618
|
+
],
|
|
619
|
+
lifecycle: {
|
|
620
|
+
trialToPayRate: 0.30,
|
|
621
|
+
upgradeRate: 0.08,
|
|
622
|
+
downgradeRate: 0.03,
|
|
623
|
+
churnRate: 0.05,
|
|
624
|
+
winBackRate: 0.10,
|
|
625
|
+
winBackDelay: 21,
|
|
626
|
+
paymentFailureRate: 0.02,
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
|
|
630
|
+
// -- Phase 2: Geo -----------------------------------------
|
|
631
|
+
geo: {
|
|
632
|
+
sticky: true,
|
|
633
|
+
regions: [
|
|
634
|
+
{
|
|
635
|
+
name: "us",
|
|
636
|
+
countries: ["US"],
|
|
637
|
+
weight: 40,
|
|
638
|
+
timezoneOffset: -5,
|
|
639
|
+
properties: { currency: "USD", locale: "en-US" },
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
name: "eu",
|
|
643
|
+
countries: ["GB", "DE", "FR", "NL"],
|
|
644
|
+
weight: 30,
|
|
645
|
+
timezoneOffset: 1,
|
|
646
|
+
properties: { currency: "EUR", locale: "en-EU" },
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
name: "india",
|
|
650
|
+
countries: ["IN"],
|
|
651
|
+
weight: 15,
|
|
652
|
+
timezoneOffset: 5.5,
|
|
653
|
+
properties: { currency: "INR", locale: "en-IN" },
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
name: "rest",
|
|
657
|
+
countries: ["SG", "AU", "JP"],
|
|
658
|
+
weight: 15,
|
|
659
|
+
timezoneOffset: 8,
|
|
660
|
+
properties: { currency: "SGD", locale: "en-SG" },
|
|
661
|
+
},
|
|
662
|
+
],
|
|
663
|
+
},
|
|
664
|
+
|
|
665
|
+
// -- Phase 2: Features ------------------------------------
|
|
666
|
+
features: [
|
|
667
|
+
{
|
|
668
|
+
name: "copilot_integration",
|
|
669
|
+
launchDay: 30,
|
|
670
|
+
adoptionCurve: "fast",
|
|
671
|
+
property: "ai_assist",
|
|
672
|
+
values: ["manual", "copilot"],
|
|
673
|
+
defaultBefore: "manual",
|
|
674
|
+
affectsEvents: ["pull request created", "code review completed"],
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
name: "deployment_preview",
|
|
678
|
+
launchDay: 50,
|
|
679
|
+
adoptionCurve: { k: 0.08, midpoint: 25 },
|
|
680
|
+
property: "preview_enabled",
|
|
681
|
+
values: [false, true],
|
|
682
|
+
defaultBefore: false,
|
|
683
|
+
affectsEvents: ["deployment completed"],
|
|
684
|
+
},
|
|
685
|
+
],
|
|
686
|
+
|
|
687
|
+
// -- Phase 2: Anomalies -----------------------------------
|
|
688
|
+
anomalies: {
|
|
689
|
+
extremeValues: [
|
|
690
|
+
{
|
|
691
|
+
event: "build completed",
|
|
692
|
+
property: "build_duration_sec",
|
|
693
|
+
frequency: 0.003,
|
|
694
|
+
multiplier: 10,
|
|
695
|
+
},
|
|
696
|
+
],
|
|
697
|
+
bursts: [
|
|
698
|
+
{
|
|
699
|
+
event: "alert triggered",
|
|
700
|
+
day: 42,
|
|
701
|
+
duration: 0.08,
|
|
702
|
+
count: 500,
|
|
703
|
+
injectProps: { alert_tag: "outage_alerts" },
|
|
704
|
+
},
|
|
705
|
+
],
|
|
706
|
+
},
|
|
707
|
+
|
|
708
|
+
// -- Hook Function ----------------------------------------
|
|
709
|
+
hook: function (record, type, meta) {
|
|
710
|
+
// -- HOOK 7: DEVOPS LEAD PROFILE ENRICHMENT (user) --------
|
|
711
|
+
// DevOps leads get team_size 10-50 and repos_connected 5-20.
|
|
712
|
+
// Platform engineers get moderate boosts. Others stay at defaults.
|
|
713
|
+
if (type === "user") {
|
|
714
|
+
if (record.segment === "devops") {
|
|
715
|
+
record.team_size = chance.integer({ min: 10, max: 50 });
|
|
716
|
+
record.repos_connected = chance.integer({ min: 5, max: 20 });
|
|
717
|
+
record.experience_level = "senior";
|
|
718
|
+
} else if (record.segment === "platform_eng") {
|
|
719
|
+
record.team_size = chance.integer({ min: 5, max: 25 });
|
|
720
|
+
record.repos_connected = chance.integer({ min: 3, max: 15 });
|
|
721
|
+
record.experience_level = chance.pickone(["mid", "senior"]);
|
|
722
|
+
} else if (record.segment === "junior") {
|
|
723
|
+
record.team_size = chance.integer({ min: 1, max: 8 });
|
|
724
|
+
record.repos_connected = chance.integer({ min: 0, max: 3 });
|
|
725
|
+
record.experience_level = "junior";
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// -- HOOK 8: ENTERPRISE BUILD-DEPLOY FUNNEL LIFT (funnel-pre)
|
|
730
|
+
// Enterprise subscribers convert 1.5x through build-deploy pipeline.
|
|
731
|
+
// Business subscribers get a smaller lift.
|
|
732
|
+
if (type === "funnel-pre") {
|
|
733
|
+
if (meta && meta.profile) {
|
|
734
|
+
const tier = meta.profile.subscription_tier;
|
|
735
|
+
if (tier === "enterprise") {
|
|
736
|
+
record.conversionRate = Math.min(record.conversionRate * 1.5, 95);
|
|
737
|
+
} else if (tier === "business") {
|
|
738
|
+
record.conversionRate = Math.min(record.conversionRate * 1.2, 90);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// -- HOOK 1: BUILD FAILURE CASCADE (event) ----------------
|
|
744
|
+
// Failed builds get 2x duration (retries take longer).
|
|
745
|
+
if (type === "event") {
|
|
746
|
+
if (record.event === "build completed" && record.build_status === "failed") {
|
|
747
|
+
record.build_duration_sec = Math.floor((record.build_duration_sec || 240) * 2);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// -- HOOK 2: NIGHT DEPLOY RISK (event) ----------------
|
|
751
|
+
// Deployments between 10PM-6AM get forced to "failed" 40% of the time.
|
|
752
|
+
if (record.event === "deployment completed") {
|
|
753
|
+
const hour = dayjs(record.time).hour();
|
|
754
|
+
if (hour >= 22 || hour < 6) {
|
|
755
|
+
record.is_night_deploy = true;
|
|
756
|
+
if (chance.bool({ likelihood: 40 })) {
|
|
757
|
+
record.deploy_status = "failed";
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// -- EVERYTHING HOOKS -------------------------------------
|
|
764
|
+
if (type === "everything") {
|
|
765
|
+
const events = record;
|
|
766
|
+
if (!events.length) return record;
|
|
767
|
+
|
|
768
|
+
// -- HOOK 3: COPILOT PR VELOCITY ----------------------
|
|
769
|
+
// Users with ai_assist="copilot" on any PR get 1.5x more PRs.
|
|
770
|
+
const hasCopilot = events.some(e =>
|
|
771
|
+
(e.event === "pull request created" || e.event === "code review completed") && e.ai_assist === "copilot"
|
|
772
|
+
);
|
|
773
|
+
if (hasCopilot) {
|
|
774
|
+
const prEvents = events.filter(e => e.event === "pull request created");
|
|
775
|
+
const extraCount = Math.floor(prEvents.length * 0.5);
|
|
776
|
+
for (let i = 0; i < extraCount; i++) {
|
|
777
|
+
const templateEvent = prEvents[i % prEvents.length];
|
|
778
|
+
if (templateEvent) {
|
|
779
|
+
events.push({
|
|
780
|
+
...templateEvent,
|
|
781
|
+
time: dayjs(templateEvent.time).add(chance.integer({ min: 1, max: 12 }), "hours").toISOString(),
|
|
782
|
+
user_id: templateEvent.user_id,
|
|
783
|
+
ai_assist: "copilot",
|
|
784
|
+
files_changed: chance.integer({ min: 1, max: 30 }),
|
|
785
|
+
lines_added: chance.integer({ min: 10, max: 800 }),
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// -- HOOK 4: ON-CALL ROTATION FATIGUE -----------------
|
|
792
|
+
// Users with >20 alerts get increasing response_time_minutes.
|
|
793
|
+
const alertCount = events.filter(e => e.event === "alert triggered").length;
|
|
794
|
+
if (alertCount > 20) {
|
|
795
|
+
const fatigueMultiplier = 1 + Math.min(alertCount / 20, 3);
|
|
796
|
+
events.forEach(e => {
|
|
797
|
+
if (e.event === "incident resolved" && e.response_time_minutes) {
|
|
798
|
+
e.response_time_minutes = Math.floor(e.response_time_minutes * fatigueMultiplier);
|
|
799
|
+
}
|
|
800
|
+
if (e.event === "incident created" && e.response_time_minutes) {
|
|
801
|
+
e.response_time_minutes = Math.floor(e.response_time_minutes * fatigueMultiplier);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// -- HOOK 5: OPEN SOURCE -> PAID CONVERSION -----------
|
|
807
|
+
// OSS users with >15 events get 30% of events converted to "team" tier.
|
|
808
|
+
if (meta && meta.profile && meta.profile.segment === "oss_user" && events.length > 15) {
|
|
809
|
+
const conversionPoint = Math.floor(events.length * 0.7);
|
|
810
|
+
for (let i = conversionPoint; i < events.length; i++) {
|
|
811
|
+
events[i].subscription_tier = "team";
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// -- HOOK 6: POST-OUTAGE RECOVERY ---------------------
|
|
816
|
+
// After outage ends (day 42.25), deployment events get cloned.
|
|
817
|
+
const OUTAGE_END = DATASET_START.add(42.25, "days");
|
|
818
|
+
const RECOVERY_END = DATASET_START.add(46, "days");
|
|
819
|
+
const deployEvents = events.filter(e => {
|
|
820
|
+
if (e.event !== "deployment completed") return false;
|
|
821
|
+
const t = dayjs(e.time);
|
|
822
|
+
return t.isAfter(OUTAGE_END) && t.isBefore(RECOVERY_END);
|
|
823
|
+
});
|
|
824
|
+
deployEvents.forEach(dep => {
|
|
825
|
+
if (chance.bool({ likelihood: 50 })) {
|
|
826
|
+
events.push({
|
|
827
|
+
...dep,
|
|
828
|
+
time: dayjs(dep.time).add(chance.integer({ min: 1, max: 6 }), "hours").toISOString(),
|
|
829
|
+
user_id: dep.user_id,
|
|
830
|
+
deploy_status: chance.pickone(["success", "success", "rolled_back"]),
|
|
831
|
+
environment: "production",
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
return record;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return record;
|
|
840
|
+
},
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
export default config;
|