@ak--47/dungeon-master 1.5.0 → 1.5.2

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 (66) hide show
  1. package/.claude/skills/create-dungeon/SKILL.md +139 -46
  2. package/.claude/skills/verify-dungeon/references/counting-semantics.md +31 -6
  3. package/.claude/skills/verify-dungeon/references/sql-recipes.md +44 -25
  4. package/.claude/skills/write-hooks/SKILL.md +31 -3
  5. package/CHANGELOG.md +85 -0
  6. package/HOOKS.md +13 -0
  7. package/dungeons/technical/ad-spend.js +41 -49
  8. package/dungeons/technical/anonymous-users.js +38 -36
  9. package/dungeons/technical/array-of-object-lookup.js +136 -153
  10. package/dungeons/technical/datagen-v15-verify.js +24 -11
  11. package/dungeons/technical/experiments.js +42 -40
  12. package/dungeons/technical/foobar.js +114 -118
  13. package/dungeons/technical/group-analytics.js +42 -40
  14. package/dungeons/technical/hook-helpers-verify.js +69 -50
  15. package/dungeons/technical/identity-model-verify.js +22 -12
  16. package/dungeons/technical/mirror-strategies.js +37 -39
  17. package/dungeons/technical/nested-objects.js +119 -118
  18. package/dungeons/technical/pattern-aggregate-by-bin.js +21 -8
  19. package/dungeons/technical/pattern-attributed-by-source.js +23 -9
  20. package/dungeons/technical/pattern-frequency-by-frequency.js +21 -8
  21. package/dungeons/technical/pattern-funnel-frequency.js +30 -15
  22. package/dungeons/technical/pattern-ttc-by-segment.js +21 -8
  23. package/dungeons/technical/retention-cadence.js +115 -112
  24. package/dungeons/technical/sanity.js +86 -80
  25. package/dungeons/technical/scale-test.js +34 -38
  26. package/dungeons/technical/scd.js +111 -128
  27. package/dungeons/technical/simple.js +134 -141
  28. package/dungeons/technical/simplest.js +54 -62
  29. package/dungeons/technical/text-generation.js +110 -146
  30. package/dungeons/vertical/ai-platform.js +296 -333
  31. package/dungeons/vertical/community.js +284 -255
  32. package/dungeons/vertical/crypto.js +395 -391
  33. package/dungeons/vertical/dating.js +411 -378
  34. package/dungeons/vertical/devtools.js +336 -298
  35. package/dungeons/vertical/ecommerce.js +316 -394
  36. package/dungeons/vertical/education.js +369 -325
  37. package/dungeons/vertical/fintech.js +358 -325
  38. package/dungeons/vertical/fitness.js +335 -291
  39. package/dungeons/vertical/food-delivery.js +343 -307
  40. package/dungeons/vertical/gaming.js +480 -444
  41. package/dungeons/vertical/healthcare.js +306 -262
  42. package/dungeons/vertical/insurance-application.js +427 -409
  43. package/dungeons/vertical/logistics.js +271 -252
  44. package/dungeons/vertical/marketplace.js +333 -323
  45. package/dungeons/vertical/media.js +382 -335
  46. package/dungeons/vertical/real-estate.js +395 -346
  47. package/dungeons/vertical/sass.js +319 -333
  48. package/dungeons/vertical/social.js +368 -316
  49. package/dungeons/vertical/travel.js +297 -295
  50. package/index.js +46 -4
  51. package/lib/core/config-validator.js +126 -28
  52. package/lib/generators/funnels.js +4 -1
  53. package/lib/orchestrators/mixpanel-sender.js +7 -0
  54. package/lib/orchestrators/user-loop.js +132 -31
  55. package/lib/templates/defaults.js +59 -59
  56. package/lib/templates/macro-presets.js +14 -2
  57. package/lib/utils/dataset-context.js +103 -0
  58. package/lib/utils/retention-curve.js +140 -0
  59. package/lib/utils/utils.js +149 -38
  60. package/lib/verify/counting.js +40 -0
  61. package/lib/verify/emulate-breakdown.js +20 -1
  62. package/lib/verify/index.js +1 -0
  63. package/lib/verify/schema-validator.js +3 -1
  64. package/package.json +11 -2
  65. package/scripts/run-dungeon.mjs +12 -1
  66. package/types.d.ts +117 -1
package/types.d.ts CHANGED
@@ -12,10 +12,66 @@ type Primitives = string | number | boolean | Date | Record<string, any>;
12
12
  */
13
13
  export type ValueValid = Primitives | ValueValid[] | (() => ValueValid);
14
14
 
15
+ /**
16
+ * v1.5.1 — credentials sub-object. Groups Mixpanel project credentials. Top-level
17
+ * `token` / `region` / etc. remain functional as a back-compat alias; when both
18
+ * are set, the top-level value wins with a verbose warning.
19
+ */
20
+ export interface DungeonCredentials {
21
+ token?: string;
22
+ region?: 'US' | 'EU' | 'IN';
23
+ serviceAccount?: string;
24
+ serviceSecret?: string;
25
+ projectId?: string;
26
+ }
27
+
28
+ /**
29
+ * v1.5.1 — switches sub-object. Groups data-shape booleans. Top-level keys
30
+ * remain functional as a back-compat alias; same precedence rules as
31
+ * `DungeonCredentials`.
32
+ */
33
+ export interface DungeonSwitches {
34
+ hasLocation?: boolean;
35
+ hasCampaigns?: boolean;
36
+ hasAdSpend?: boolean;
37
+ hasSessionIds?: boolean;
38
+ hasAvatar?: boolean;
39
+ hasIOSDevices?: boolean;
40
+ hasAndroidDevices?: boolean;
41
+ hasDesktopDevices?: boolean;
42
+ hasBrowser?: boolean;
43
+ isAnonymous?: boolean;
44
+ alsoInferFunnels?: boolean;
45
+ hasAttributionFlags?: boolean;
46
+ }
47
+
48
+ /**
49
+ * v1.5.1 — identity sub-object. Groups identity-model knobs. Top-level
50
+ * `avgDevicePerUser` / `sessionTimeout` remain functional as a back-compat
51
+ * alias.
52
+ *
53
+ * `hasAnonIds` is DEPRECATED — when present here, it maps to
54
+ * `avgDevicePerUser: 1` with a verbose warning. Use `avgDevicePerUser` instead.
55
+ */
56
+ export interface DungeonIdentity {
57
+ avgDevicePerUser?: number;
58
+ sessionTimeout?: number;
59
+ /** @deprecated v1.5.1 — use `avgDevicePerUser: 1` instead. */
60
+ hasAnonIds?: boolean;
61
+ }
62
+
15
63
  /**
16
64
  * main config object for the entire data generation
17
65
  */
18
66
  export interface Dungeon {
67
+ // ── v1.5.1 sub-object grouping (optional) ──
68
+ /** v1.5.1 — credentials sub-object. See `DungeonCredentials`. */
69
+ credentials?: DungeonCredentials;
70
+ /** v1.5.1 — switches sub-object. See `DungeonSwitches`. */
71
+ switches?: DungeonSwitches;
72
+ /** v1.5.1 — identity sub-object. See `DungeonIdentity`. */
73
+ identity?: DungeonIdentity;
74
+
19
75
  // ── Core Parameters ──
20
76
  /** Optional dungeon version. Not used by the engine — serves as metadata for tracking revisions when configs are saved/shared. */
21
77
  version?: string | number;
@@ -25,6 +81,19 @@ export interface Dungeon {
25
81
  token?: string;
26
82
  /** RNG seed for reproducible output. Same seed + concurrency=1 = identical data. */
27
83
  seed?: string;
84
+ /**
85
+ * Optional separate RNG seed dedicated to `distinct_id` generation.
86
+ *
87
+ * When set, two runs with the same `userSeed` but different `seed` produce
88
+ * the SAME pool of user IDs but DIFFERENT events. Designed for sharded /
89
+ * massively-parallel runs (e.g., Cloud Run Job fan-out) that need cross-shard
90
+ * user identity — every shard generating bucket N pulls from the same 3M
91
+ * user IDs while producing unique events of its own.
92
+ *
93
+ * When unset, the engine falls back to `seed` for user-id generation —
94
+ * existing dungeons stay byte-identical.
95
+ */
96
+ userSeed?: string;
28
97
  /**
29
98
  * Number of days the dataset spans. Default: 30.
30
99
  *
@@ -303,6 +372,32 @@ export interface Dungeon {
303
372
  * purpose; the v1.5 validator strict-clamps to `floor(numDays * 0.5)` with a warning.
304
373
  */
305
374
  avgActiveDaysPerUser?: number;
375
+ /**
376
+ * v1.5.1 — target retention shape. Anchor points `day1`, `day7`, `day30`
377
+ * etc. define the per-day-offset weight a user is active. When set, biases
378
+ * `buildActiveDayPlan` toward the curve and the effective
379
+ * `avgActiveDaysPerUser` is derived from the curve's sum across the user's
380
+ * window (curve wins over an explicit `avgActiveDaysPerUser`).
381
+ *
382
+ * - `type`: `'logarithmic'` (default, real-world retention shape) or `'linear'`.
383
+ * - `dayN` keys: fraction active on day N from birth (0..1). Day 0 is
384
+ * implicitly 1.0 (every user is active on their birth day).
385
+ * - Days beyond the largest anchor extrapolate from the last segment.
386
+ *
387
+ * Example: `{ day1: 0.40, day7: 0.20, day30: 0.08 }` produces a curve that
388
+ * approximates a typical product's 30-day retention.
389
+ */
390
+ retentionCurve?: {
391
+ type?: 'logarithmic' | 'linear';
392
+ day1?: number;
393
+ day3?: number;
394
+ day7?: number;
395
+ day14?: number;
396
+ day30?: number;
397
+ day60?: number;
398
+ day90?: number;
399
+ [dayKey: string]: number | 'logarithmic' | 'linear' | undefined;
400
+ };
306
401
  /**
307
402
  * Maximum number of UTM-stamped events per user. Matches Mixpanel's `TOUCHPOINTS_LIMIT`
308
403
  * (`backend/libquery/properties_over_time/attributed_value_reader.cpp` line 16).
@@ -1115,6 +1210,15 @@ export interface UserProfile {
1115
1210
  avatar?: string;
1116
1211
  created: string | undefined;
1117
1212
  distinct_id: string;
1213
+ /**
1214
+ * v1.5.1: when `true`, the engine considers this profile "anonymous" — the
1215
+ * user never reached an `isAuthEvent` step. Profile still exists in
1216
+ * `userProfilesData` (so hooks and downstream tools can see the full
1217
+ * population), but `mixpanel-sender` filters it out before pushing to
1218
+ * `/engage`. Hooks can rescue by deleting the flag inside the `everything`
1219
+ * hook.
1220
+ */
1221
+ _drop?: boolean;
1118
1222
  [key: string]: ValueValid;
1119
1223
  }
1120
1224
 
@@ -1200,6 +1304,12 @@ export type Result = {
1200
1304
  userCount?: number;
1201
1305
  groupCount?: number;
1202
1306
  avgEPS?: number;
1307
+ /**
1308
+ * v1.5.1: count of profiles eligible for Mixpanel `/engage` push (i.e., not
1309
+ * flagged with `_drop: true`). Anonymous non-converters carry `_drop: true`
1310
+ * so `userProfilesData.length - profilesPushed` = dropped profile count.
1311
+ */
1312
+ profilesPushed?: number;
1203
1313
  /** Progress callback summary. Only present when `onProgress` was provided. */
1204
1314
  progress?: ProgressSummary;
1205
1315
  };
@@ -1750,7 +1860,7 @@ declare module '@ak--47/dungeon-master/hook-patterns' {
1750
1860
  * | `attributedBy` | `conversionEvent`, `attributionEvent`, `attributionProperty` | `model` (default: `'lastTouch'`) |
1751
1861
  */
1752
1862
  export interface EmulateOptions {
1753
- type: 'frequencyByFrequency' | 'funnelFrequency' | 'aggregatePerUser' | 'timeToConvert' | 'attributedBy' | 'sessionMetrics' | 'retention';
1863
+ type: 'frequencyByFrequency' | 'funnelFrequency' | 'aggregatePerUser' | 'timeToConvert' | 'attributedBy' | 'sessionMetrics' | 'retention' | 'distinctCount';
1754
1864
  metricEvent?: string;
1755
1865
  breakdownByFrequencyOf?: string;
1756
1866
  perUser?: boolean;
@@ -1815,6 +1925,10 @@ export interface EmulateOptions {
1815
1925
  * pattern requires COMPOUNDED retention which is not supported here).
1816
1926
  */
1817
1927
  birthCanRetain?: boolean;
1928
+
1929
+ // v1.5.1 distinctCount extensions
1930
+ /** Optional cap on the number of top-N values returned in `top_values`. Defaults to 25 (matches Mixpanel UI). */
1931
+ topN?: number;
1818
1932
  }
1819
1933
 
1820
1934
  /** v1.5.0 — config for `emulateBreakdown({ type: 'retention' })`. */
@@ -1906,6 +2020,8 @@ declare module '@ak--47/dungeon-master/utils' {
1906
2020
  export function weighNumRange(min: number, max: number, skew?: number, size?: number): number[];
1907
2021
  export function pickAWinner(items: string[], mostChosenIndex?: number): () => string[];
1908
2022
  export function initChance(seed?: string): unknown;
2023
+ export function initUserChance(seed?: string): unknown;
2024
+ export function getUserChance(): unknown;
1909
2025
  export function TimeSoup(earliestTime: number, latestTime: number, peaks?: number, deviation?: number, mean?: number, dayOfWeekWeights?: number[] | null, hourOfDayWeights?: number[] | null): number;
1910
2026
  export function weighArray<T>(items: T[]): T[];
1911
2027
  export function generateUser(user_id: string, opts: Record<string, unknown>): Record<string, unknown>;