@ak--47/dungeon-master 1.5.4 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/analyze-soup/SKILL.md +21 -12
- package/.claude/skills/create-dungeon/SKILL.md +65 -5
- package/.claude/skills/create-project/SKILL.md +1 -1
- package/.claude/skills/create-project/provision.mjs +57 -10
- package/.claude/skills/verify-dungeon/SKILL.md +56 -25
- package/.claude/skills/verify-dungeon/references/counting-semantics.md +26 -6
- package/.claude/skills/verify-dungeon/references/report-format.md +14 -8
- package/.claude/skills/write-hooks/SKILL.md +156 -18
- package/CHANGELOG.md +183 -0
- package/HOOKS.md +566 -112
- package/README.md +28 -9
- package/docs/guides/1.3.0-upgrade-guide.md +262 -0
- package/docs/guides/1.3.1-upgrade-guide.md +328 -0
- package/docs/guides/1.4.0-upgrade-guide.md +130 -0
- package/docs/guides/1.4.1-upgrade-guide.md +125 -0
- package/docs/guides/1.4.3-upgrade-guide.md +200 -0
- package/docs/guides/1.4.4-upgrade-guide.md +95 -0
- package/docs/guides/1.5.0-upgrade-guide.md +426 -0
- package/docs/guides/1.5.1-upgrade-guide.md +178 -0
- package/docs/guides/1.5.3-upgrade-guide.md +117 -0
- package/docs/guides/1.6.0-upgrade-guide.md +204 -0
- package/dungeons/technical/stories-verify.js +152 -0
- package/dungeons/vertical/README.md +154 -0
- package/dungeons/vertical/ai-platform/ai-platform.js +1424 -0
- package/dungeons/vertical/ai-platform/ai-platform.sql +184 -0
- package/dungeons/vertical/ai-platform/ai-platform.verify.mjs +71 -0
- package/dungeons/vertical/community/community.js +1589 -0
- package/dungeons/vertical/community/community.sql +216 -0
- package/dungeons/vertical/community/community.verify.mjs +72 -0
- package/dungeons/vertical/crypto/crypto.js +1483 -0
- package/dungeons/vertical/crypto/crypto.sql +194 -0
- package/dungeons/vertical/crypto/crypto.verify.mjs +78 -0
- package/dungeons/vertical/dating/dating.js +1540 -0
- package/dungeons/vertical/dating/dating.sql +288 -0
- package/dungeons/vertical/dating/dating.verify.mjs +72 -0
- package/dungeons/vertical/devtools/devtools.js +1524 -0
- package/dungeons/vertical/devtools/devtools.sql +161 -0
- package/dungeons/vertical/devtools/devtools.verify.mjs +73 -0
- package/dungeons/vertical/ecommerce/ecommerce.js +1480 -0
- package/dungeons/vertical/ecommerce/ecommerce.sql +218 -0
- package/dungeons/vertical/ecommerce/ecommerce.verify.mjs +70 -0
- package/dungeons/vertical/education/education.js +1763 -0
- package/dungeons/vertical/education/education.sql +243 -0
- package/dungeons/vertical/education/education.verify.mjs +75 -0
- package/dungeons/vertical/fintech/fintech.js +1410 -0
- package/dungeons/vertical/fintech/fintech.sql +175 -0
- package/dungeons/vertical/fintech/fintech.verify.mjs +70 -0
- package/dungeons/vertical/fitness/fitness.js +1495 -0
- package/dungeons/vertical/fitness/fitness.sql +206 -0
- package/dungeons/vertical/fitness/fitness.verify.mjs +71 -0
- package/dungeons/vertical/food-delivery/food-delivery.js +1641 -0
- package/dungeons/vertical/food-delivery/food-delivery.sql +183 -0
- package/dungeons/vertical/food-delivery/food-delivery.verify.mjs +75 -0
- package/dungeons/vertical/gaming/gaming.js +1741 -0
- package/dungeons/vertical/gaming/gaming.sql +249 -0
- package/dungeons/vertical/gaming/gaming.verify.mjs +70 -0
- package/dungeons/vertical/healthcare/healthcare.js +1488 -0
- package/dungeons/vertical/healthcare/healthcare.sql +193 -0
- package/dungeons/vertical/healthcare/healthcare.verify.mjs +72 -0
- package/dungeons/vertical/insurance-application/insurance-application.js +1852 -0
- package/dungeons/vertical/insurance-application/insurance-application.sql +174 -0
- package/dungeons/vertical/insurance-application/insurance-application.verify.mjs +74 -0
- package/dungeons/vertical/logistics/logistics.js +1541 -0
- package/dungeons/vertical/logistics/logistics.sql +205 -0
- package/dungeons/vertical/logistics/logistics.verify.mjs +73 -0
- package/dungeons/vertical/marketplace/marketplace.js +1438 -0
- package/dungeons/vertical/marketplace/marketplace.sql +148 -0
- package/dungeons/vertical/marketplace/marketplace.verify.mjs +74 -0
- package/dungeons/vertical/{media.js → media/media.js} +557 -58
- package/dungeons/vertical/media/media.sql +173 -0
- package/dungeons/vertical/media/media.verify.mjs +74 -0
- package/dungeons/vertical/real-estate/real-estate.js +1629 -0
- package/dungeons/vertical/real-estate/real-estate.sql +167 -0
- package/dungeons/vertical/real-estate/real-estate.verify.mjs +75 -0
- package/dungeons/vertical/sass/sass.js +1733 -0
- package/dungeons/vertical/sass/sass.sql +173 -0
- package/dungeons/vertical/sass/sass.verify.mjs +74 -0
- package/dungeons/vertical/social/social.js +1456 -0
- package/dungeons/vertical/social/social.sql +160 -0
- package/dungeons/vertical/social/social.verify.mjs +76 -0
- package/dungeons/vertical/streaming/streaming.js +677 -0
- package/dungeons/vertical/streaming/streaming.sql +101 -0
- package/dungeons/vertical/streaming/streaming.verify.mjs +75 -0
- package/dungeons/vertical/support-desk/support-desk.js +741 -0
- package/dungeons/vertical/support-desk/support-desk.sql +137 -0
- package/dungeons/vertical/support-desk/support-desk.verify.mjs +75 -0
- package/dungeons/vertical/travel/travel.js +1603 -0
- package/dungeons/vertical/travel/travel.sql +236 -0
- package/dungeons/vertical/travel/travel.verify.mjs +72 -0
- package/index.js +2 -2
- package/lib/core/config-validator.js +37 -2
- package/lib/core/context.js +4 -2
- package/lib/core/dungeon-loader.js +20 -15
- package/lib/core/dungeon-to-json.js +22 -4
- package/lib/core/storage.js +3 -3
- package/lib/generators/funnels.js +36 -9
- package/lib/hook-helpers/cohort.js +53 -0
- package/lib/hook-helpers/index.js +8 -0
- package/lib/hook-helpers/mutate.js +1 -1
- package/lib/hook-helpers/shape.js +319 -0
- package/lib/hook-patterns/aggregate-per-user-by-bin.js +13 -5
- package/lib/hook-patterns/attributed-by-source.js +60 -58
- package/lib/hook-patterns/frequency-by-frequency.js +14 -4
- package/lib/hook-patterns/funnel-frequency-breakdown.js +24 -12
- package/lib/hook-patterns/index.js +1 -1
- package/lib/hook-patterns/time-to-convert-by-segment.js +75 -13
- package/lib/orchestrators/user-loop.js +41 -0
- package/lib/templates/story-spec.schema.json +175 -0
- package/lib/utils/json-evaluator.js +1 -1
- package/lib/utils/utils.js +5 -4
- package/lib/verify/coerce.js +186 -0
- package/lib/verify/counting.js +270 -32
- package/lib/verify/emulate-breakdown.js +1285 -203
- package/lib/verify/first-time.js +89 -0
- package/lib/verify/flows.js +679 -0
- package/lib/verify/formula.js +259 -0
- package/lib/verify/funnel-engine.js +697 -113
- package/lib/verify/index.js +32 -4
- package/lib/verify/sessionize.js +275 -0
- package/lib/verify/story-runner.js +483 -0
- package/lib/verify/verify-dungeon.js +59 -37
- package/package.json +2 -1
- package/scripts/extract-dungeon-schema.mjs +2 -2
- package/scripts/run-many.mjs +2 -2
- package/scripts/verify-runner.mjs +2 -2
- package/scripts/verify-stories.mjs +244 -0
- package/types.d.ts +296 -30
- package/dungeons/vertical/ai-platform.js +0 -818
- package/dungeons/vertical/community.js +0 -966
- package/dungeons/vertical/crypto.js +0 -895
- package/dungeons/vertical/dating.js +0 -830
- package/dungeons/vertical/devtools.js +0 -1050
- package/dungeons/vertical/ecommerce.js +0 -811
- package/dungeons/vertical/education.js +0 -964
- package/dungeons/vertical/fintech.js +0 -946
- package/dungeons/vertical/fitness.js +0 -935
- package/dungeons/vertical/food-delivery.js +0 -934
- package/dungeons/vertical/gaming.js +0 -1120
- package/dungeons/vertical/healthcare.js +0 -945
- package/dungeons/vertical/insurance-application.js +0 -1039
- package/dungeons/vertical/logistics.js +0 -986
- package/dungeons/vertical/marketplace.js +0 -1019
- package/dungeons/vertical/real-estate.js +0 -872
- package/dungeons/vertical/sass.js +0 -995
- package/dungeons/vertical/social.js +0 -904
- package/dungeons/vertical/travel.js +0 -930
package/types.d.ts
CHANGED
|
@@ -147,7 +147,11 @@ export interface Dungeon {
|
|
|
147
147
|
region?: "US" | "EU";
|
|
148
148
|
/** User generation concurrency. Default: 1. Values > 1 break seed reproducibility and provide no performance benefit (CPU-bound). */
|
|
149
149
|
concurrency?: number;
|
|
150
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Number of records before auto-flushing to disk. Prevents OOM for large datasets.
|
|
152
|
+
* Default: 2,500,000. Auto-enabled at 1,000,000 when the projected event count
|
|
153
|
+
* reaches 2M and no explicit value is set (clamped lower when avgPropsPerEvent > 18).
|
|
154
|
+
*/
|
|
151
155
|
batchSize?: number;
|
|
152
156
|
|
|
153
157
|
// ── Mixpanel Import Credentials (for SCD import) ──
|
|
@@ -1029,9 +1033,10 @@ export interface Funnel {
|
|
|
1029
1033
|
* - `ExperimentConfig` object — custom variant names, conversion/TTC multipliers, temporal gating,
|
|
1030
1034
|
* and distribution weights.
|
|
1031
1035
|
*
|
|
1032
|
-
* Variant assignment is **deterministic per user
|
|
1033
|
-
* user is in the same variant across all funnel runs.
|
|
1034
|
-
*
|
|
1036
|
+
* Variant assignment is **sticky by default** — deterministic per user (hash of user_id +
|
|
1037
|
+
* experiment name), so the same user is in the same variant across all funnel runs. Set
|
|
1038
|
+
* `sticky: false` on the config object to re-randomize the variant on every funnel pass.
|
|
1039
|
+
* `$experiment_started` is prepended to the sequence for every post-start-date funnel run.
|
|
1035
1040
|
*
|
|
1036
1041
|
* Hook meta (`meta.experiment`) exposes the resolved variant in `funnel-pre` and `funnel-post`
|
|
1037
1042
|
* hooks, enabling variant-specific story injection.
|
|
@@ -1077,7 +1082,7 @@ export interface Funnel {
|
|
|
1077
1082
|
}>;
|
|
1078
1083
|
|
|
1079
1084
|
/** @internal Resolved experiment config set by config-validator. */
|
|
1080
|
-
_experiment?: { name: string; variants: Array<{ name: string; conversionMultiplier: number; ttcMultiplier: number; weight: number }>; startUnix: number | null };
|
|
1085
|
+
_experiment?: { name: string; variants: Array<{ name: string; conversionMultiplier: number; ttcMultiplier: number; weight: number }>; startUnix: number | null; sticky: boolean };
|
|
1081
1086
|
/** @internal Set by funnels.js during experiment handling. */
|
|
1082
1087
|
_experimentName?: string;
|
|
1083
1088
|
/** @internal Set by funnels.js during experiment handling. */
|
|
@@ -1151,6 +1156,15 @@ export interface ExperimentConfig {
|
|
|
1151
1156
|
* Default: 0 (entire dataset).
|
|
1152
1157
|
*/
|
|
1153
1158
|
startDaysBeforeEnd?: number;
|
|
1159
|
+
/**
|
|
1160
|
+
* Sticky bucketing. When `true` (default), variant assignment is a deterministic
|
|
1161
|
+
* hash of user_id + experiment name — once a user falls into a variant they get
|
|
1162
|
+
* that variant on every funnel pass (matches Mixpanel experiment SDK behavior and
|
|
1163
|
+
* makes variant trends verifiable). When `false`, the variant is re-rolled on
|
|
1164
|
+
* every funnel pass using the seeded RNG, so a multi-pass user may see different
|
|
1165
|
+
* variants across passes.
|
|
1166
|
+
*/
|
|
1167
|
+
sticky?: boolean;
|
|
1154
1168
|
}
|
|
1155
1169
|
|
|
1156
1170
|
/** A single variant in an experiment. */
|
|
@@ -1380,15 +1394,29 @@ export interface Persona {
|
|
|
1380
1394
|
eventMultiplier?: number;
|
|
1381
1395
|
/** Multiplier for funnel conversion rates (1.0 = normal, 1.3 = 30% better). */
|
|
1382
1396
|
conversionModifier?: number;
|
|
1383
|
-
/**
|
|
1397
|
+
/**
|
|
1398
|
+
* Base churn rate for this persona (0-1).
|
|
1399
|
+
* @deprecated — unimplemented; no-op. The validator defaults it to 0 but no
|
|
1400
|
+
* generator reads it. Model churn with hooks instead (HOOKS.md §3 cohort
|
|
1401
|
+
* atoms + `engagementDecay`).
|
|
1402
|
+
*/
|
|
1384
1403
|
churnRate?: number;
|
|
1385
1404
|
/** Properties merged into user profiles for this persona. */
|
|
1386
1405
|
properties?: Record<string, ValueValid>;
|
|
1387
|
-
/**
|
|
1406
|
+
/**
|
|
1407
|
+
* Limit how long this persona is active (e.g., trial users active for 14 days).
|
|
1408
|
+
* @deprecated — unimplemented; no-op. Declared surface only; nothing in lib/
|
|
1409
|
+
* reads it. Bound activity windows with an `everything` hook (drop events
|
|
1410
|
+
* outside the window) instead.
|
|
1411
|
+
*/
|
|
1388
1412
|
activeWindow?: { maxDays: number };
|
|
1389
1413
|
/** Per-persona engagement decay override. */
|
|
1390
1414
|
engagementDecay?: EngagementDecay;
|
|
1391
|
-
/**
|
|
1415
|
+
/**
|
|
1416
|
+
* Per-persona soup/timing override.
|
|
1417
|
+
* @deprecated — unimplemented; no-op. Declared surface only; nothing in lib/
|
|
1418
|
+
* reads it. Use the top-level `soup` config for timing shape.
|
|
1419
|
+
*/
|
|
1392
1420
|
soupOverride?: SoupConfig;
|
|
1393
1421
|
}
|
|
1394
1422
|
|
|
@@ -1581,6 +1609,110 @@ export declare function dungeonToJSON(input: string[], options?: { includeCreden
|
|
|
1581
1609
|
export declare function extractComments(input: string): DungeonComments;
|
|
1582
1610
|
export declare function extractComments(input: string[]): DungeonComments[];
|
|
1583
1611
|
|
|
1612
|
+
// ============= Story-Spec Types (v1.6) =============
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
* Closed enum of engineered-effect shapes, seeded from the corpus's real story
|
|
1616
|
+
* types. Mirrors `lib/templates/story-spec.schema.json` (a unit test keeps the
|
|
1617
|
+
* two in sync).
|
|
1618
|
+
*/
|
|
1619
|
+
export type StoryArchetype =
|
|
1620
|
+
| "cohort-count-scale"
|
|
1621
|
+
| "cohort-prop-scale"
|
|
1622
|
+
| "temporal-inflection"
|
|
1623
|
+
| "funnel-conversion-by-segment"
|
|
1624
|
+
| "funnel-ttc-by-segment"
|
|
1625
|
+
| "retention-divergence"
|
|
1626
|
+
| "frequency-sweet-spot"
|
|
1627
|
+
| "attribution-bias"
|
|
1628
|
+
| "experiment-lift"
|
|
1629
|
+
| "lifecycle-wave"
|
|
1630
|
+
| "path-share"
|
|
1631
|
+
| "session-shape"
|
|
1632
|
+
| "composition-drift"
|
|
1633
|
+
| "bespoke";
|
|
1634
|
+
|
|
1635
|
+
/**
|
|
1636
|
+
* Verdict tiers, worst → best: INVERSE < NONE < WEAK < STRONG < NAILED.
|
|
1637
|
+
* NAILED = observed within ±10% of `target`; STRONG = passes `floor` (or
|
|
1638
|
+
* `target` when no floor); WEAK = fails floor but effect direction correct, or
|
|
1639
|
+
* cohort < `minCohort`; NONE = no measurable effect / selection empty;
|
|
1640
|
+
* INVERSE = effect direction opposite the assertion. Story verdict = worst
|
|
1641
|
+
* assertion.
|
|
1642
|
+
*/
|
|
1643
|
+
export type StoryVerdict = "NAILED" | "STRONG" | "WEAK" | "NONE" | "INVERSE";
|
|
1644
|
+
|
|
1645
|
+
/** A where-clause value: plain equality, or an explicit comparison. */
|
|
1646
|
+
export type StoryWhereValue =
|
|
1647
|
+
| string | number | boolean | null
|
|
1648
|
+
| { op: "==" | "!=" | ">=" | "<=" | ">" | "<"; value: string | number | boolean | null };
|
|
1649
|
+
|
|
1650
|
+
/** Named row-set over the breakdown result rows (all clauses AND together). */
|
|
1651
|
+
export interface StorySelect {
|
|
1652
|
+
[name: string]: { where: Record<string, StoryWhereValue> };
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* The pinned expect grammar. `metric` is one of exactly three forms: a single
|
|
1657
|
+
* ref `'<name>.<column>'`, a ratio `'<a>.<col> / <b>.<col>'`, or a difference
|
|
1658
|
+
* `'<a>.<col> - <b>.<col>'`. At most one binary operator; operands are refs or
|
|
1659
|
+
* numeric literals (at least one ref). If a selection matches multiple rows,
|
|
1660
|
+
* count-like columns sum and value-like columns error. No free-form
|
|
1661
|
+
* expressions.
|
|
1662
|
+
*/
|
|
1663
|
+
export interface StoryExpect {
|
|
1664
|
+
metric: string;
|
|
1665
|
+
op: ">=" | "<=" | ">" | "<" | "between";
|
|
1666
|
+
/** The designed value — derive from the same exported knob constants the hook uses. `[lo, hi]` only with op `'between'`. */
|
|
1667
|
+
target: number | [number, number];
|
|
1668
|
+
/** Optional STRONG bound. Omitted → `target` doubles as the floor. */
|
|
1669
|
+
floor?: number;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
/** One mechanical assertion inside a story. Requires `expect` or `assert`. */
|
|
1673
|
+
export interface StoryAssertion {
|
|
1674
|
+
/**
|
|
1675
|
+
* Byte-compatible with `emulateBreakdown` / `verifyDungeon` args — or the
|
|
1676
|
+
* `{ type: 'duckdb', sql }` escape hatch (disk mode only; `{{PREFIX}}` in
|
|
1677
|
+
* the SQL is substituted with the run's data prefix path).
|
|
1678
|
+
*/
|
|
1679
|
+
breakdown: Record<string, unknown> & { type: string; sql?: string };
|
|
1680
|
+
select?: StorySelect;
|
|
1681
|
+
expect?: StoryExpect;
|
|
1682
|
+
/**
|
|
1683
|
+
* JS-only escape hatch — discouraged; every use requires a comment
|
|
1684
|
+
* justifying why the declarative grammar can't express it. Return
|
|
1685
|
+
* `verdict` to place the result on the five-tier scale; otherwise
|
|
1686
|
+
* `pass` maps to STRONG / NONE.
|
|
1687
|
+
*/
|
|
1688
|
+
assert?: (rows: Array<Record<string, unknown>>, ctx?: Record<string, unknown>) => { pass: boolean; detail?: string; verdict?: StoryVerdict };
|
|
1689
|
+
/** Population floor — below this, the verdict caps at WEAK. */
|
|
1690
|
+
minCohort?: number;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* One story of the `stories` named export of a JS dungeon file (v1.6
|
|
1695
|
+
* story-spec). Living in the dungeon file means thresholds are computed from
|
|
1696
|
+
* the same knob constants the hook uses — the assertion can't drift from the
|
|
1697
|
+
* mechanism. Extra exports are ignored by `dungeon-loader` (zero compat
|
|
1698
|
+
* impact); `dungeon-to-json` drops them, so stories are JS-dungeon-only.
|
|
1699
|
+
* Schema: `lib/templates/story-spec.schema.json`.
|
|
1700
|
+
*/
|
|
1701
|
+
export interface DungeonStory {
|
|
1702
|
+
/** Unique story id, conventionally `<hook>-<slug>` (e.g. `'H3-fraud-bursts'`). */
|
|
1703
|
+
id: string;
|
|
1704
|
+
/** Numbered hook this story verifies — matches the HOOK STORIES doc block (`'H3'` or `'Hook 3'`). */
|
|
1705
|
+
hook: string;
|
|
1706
|
+
archetype: StoryArchetype;
|
|
1707
|
+
/** One-to-three sentence human story the data tells. */
|
|
1708
|
+
narrative: string;
|
|
1709
|
+
/** Free-form report pointer for humans and /create-project — not interpreted by the runner. */
|
|
1710
|
+
mixpanelReport?: Record<string, unknown>;
|
|
1711
|
+
assertions: StoryAssertion[];
|
|
1712
|
+
/** Documented strict-bar / limitation notes. */
|
|
1713
|
+
intentionalDeviations?: string[];
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1584
1716
|
// ============= Text Generator Types =============
|
|
1585
1717
|
|
|
1586
1718
|
/**
|
|
@@ -1911,14 +2043,39 @@ declare module '@ak--47/dungeon-master/hook-helpers' {
|
|
|
1911
2043
|
export function injectBurst(events: EventSchema[], templateEvent: EventSchema, count: number, anchorTime: number | string, spreadMs: number): void;
|
|
1912
2044
|
export function isPreAuthEvent(event: EventSchema, authTime: number | null): boolean;
|
|
1913
2045
|
export function splitByAuth(events: EventSchema[], authTime: number | null): { preAuth: EventSchema[]; postAuth: EventSchema[]; stitch: EventSchema | null };
|
|
2046
|
+
/** v1.6.0 — deterministic FNV-1a hash of any id to [0, 1). Seed-independent and stable across runs. */
|
|
2047
|
+
export function hashFloat(id: string): number;
|
|
2048
|
+
/** v1.6.0 — deterministic cohort gate: true for `pct`% of ids (0-100 scale). Nest by hashing derived ids (e.g. `uid + ':wave2'`). */
|
|
2049
|
+
export function hashCohort(id: string, pct: number): boolean;
|
|
2050
|
+
/** Clone a template event of `eventName` onto `targetDays − existing` random inactive days within the user's active window. Schema-first: no template → no-op. */
|
|
2051
|
+
export function injectOnNewDays(events: EventSchema[], eventName: string, targetDays: number, options?: { timeRange?: 'active'; overrides?: Partial<EventSchema> }): EventSchema[];
|
|
2052
|
+
/** v1.6.0 — carve a dormant window (drop value moments, or all events with `dropAll`) then append a resurrection burst cloned from the surviving value-moment template. Returns a NEW array. */
|
|
2053
|
+
export function applyLifecycleWave(events: EventSchema[], uid: string, opts: { dormantFromDay: number; dormantDays: number; valueMomentEvent: string; resurrectBurst?: number; dropAll?: boolean }): EventSchema[];
|
|
2054
|
+
/** v1.6.0 — inject an ordered event path after each anchor for a deterministic `share` of users (hash-gated). Augments in place; engine auto-sort handles ordering. */
|
|
2055
|
+
export function applyPathBias(events: EventSchema[], uid: string, opts: { anchor: string; path: string[]; share: number; gapSeconds?: [number, number] }): EventSchema[];
|
|
2056
|
+
/** v1.6.0 — rewrite the user's timestamps into deterministic session clusters (n/week, m events, bounded span) that survive query-time re-derivation. */
|
|
2057
|
+
export function applySessionShape(events: EventSchema[], uid: string, opts: { sessionsPerWeek: number; eventsPerSession: number; sessionMinutes: number }): EventSchema[];
|
|
1914
2058
|
}
|
|
1915
2059
|
|
|
1916
2060
|
declare module '@ak--47/dungeon-master/hook-patterns' {
|
|
1917
|
-
export function applyFrequencyByFrequency(events: EventSchema[], profile: Record<string, unknown> | null, opts: { cohortEvent: string; bins: Record<string, [number, number]>; targetEvent: string; multipliers: Record<string, number
|
|
1918
|
-
export function applyFunnelFrequencyBreakdown(allUserEvents: EventSchema[], profile: Record<string, unknown> | null, funnelEvents: EventSchema[], opts: { cohortEvent: string; bins: Record<string, [number, number]>; dropMultipliers: Record<string, number
|
|
1919
|
-
export function applyAggregateByBin(events: EventSchema[], profile: Record<string, unknown> | null, opts: { cohortEvent: string; bins: Record<string, [number, number]>; event: string; propertyName: string; deltas: Record<string, number
|
|
2061
|
+
export function applyFrequencyByFrequency(events: EventSchema[], profile: Record<string, unknown> | null, opts: { cohortEvent: string; bins: Record<string, [number, number]>; targetEvent: string; multipliers: Record<string, number>; binBy?: 'events' | 'distinctDays' }): { bin: string | null; delta: number };
|
|
2062
|
+
export function applyFunnelFrequencyBreakdown(allUserEvents: EventSchema[], profile: Record<string, unknown> | null, funnelEvents: EventSchema[], opts: { cohortEvent: string; bins: Record<string, [number, number]>; dropMultipliers: Record<string, number>; finalStep?: string; binBy?: 'events' | 'distinctDays' }): { bin: string | null; droppedFinal: boolean };
|
|
2063
|
+
export function applyAggregateByBin(events: EventSchema[], profile: Record<string, unknown> | null, opts: { cohortEvent: string; bins: Record<string, [number, number]>; event: string; propertyName: string; deltas: Record<string, number>; binBy?: 'events' | 'distinctDays' }): { bin: string | null; scaled: number };
|
|
2064
|
+
/**
|
|
2065
|
+
* @deprecated v1.6.0 — the funnel-post variant only reaches the Mixpanel TTC
|
|
2066
|
+
* report for `isFirstFunnel` runs (TTC measures each user's FIRST occurrence
|
|
2067
|
+
* of the step sequence). Use `applyTTCBySegmentV2` from the `everything` hook.
|
|
2068
|
+
* Still works; warns once per process.
|
|
2069
|
+
*/
|
|
1920
2070
|
export function applyTTCBySegment(funnelEvents: EventSchema[], profile: Record<string, unknown>, opts: { segmentKey: string; factors: Record<string, number> }): void;
|
|
1921
|
-
|
|
2071
|
+
/** v1.6.0 — everything-hook TTC scaling of the user's FIRST occurrence of `steps` (what Mixpanel's TTC report measures). */
|
|
2072
|
+
export function applyTTCBySegmentV2(events: EventSchema[], profile: Record<string, unknown>, opts: { segmentKey: string; factors: Record<string, number>; steps: string[]; maxGapMinutes?: number }): { segmentValue: string | null; factor: number; shifted: number };
|
|
2073
|
+
/**
|
|
2074
|
+
* v1.6.0 rewrite — overwrites ENGINE-STAMPED touch values (the user's lifetime-first
|
|
2075
|
+
* and/or -last stamped `property` event) with a weighted draw, instead of stamping
|
|
2076
|
+
* fresh values that land outside the touchpoint cap's lookback.
|
|
2077
|
+
*/
|
|
2078
|
+
export function applyAttributedBySource(events: EventSchema[], profile: Record<string, unknown> | null, opts: { weights: Record<string, number>; property?: string; model?: 'firstTouch' | 'lastTouch' | 'both' }): { overwritten: number; touches: number };
|
|
1922
2079
|
}
|
|
1923
2080
|
|
|
1924
2081
|
/**
|
|
@@ -1931,9 +2088,16 @@ declare module '@ak--47/dungeon-master/hook-patterns' {
|
|
|
1931
2088
|
* | `aggregatePerUser` | `event`, `property`, `breakdownByFrequencyOf` | `agg` (default: `'avg'`) |
|
|
1932
2089
|
* | `timeToConvert` | `fromEvent`, `toEvent`, `breakdownByUserProperty`, `profiles` | — |
|
|
1933
2090
|
* | `attributedBy` | `conversionEvent`, `attributionEvent`, `attributionProperty` | `model` (default: `'lastTouch'`) |
|
|
2091
|
+
* | `sessionMetrics` | — | `event`, `metrics`, `source` (default: `'derived'`) |
|
|
2092
|
+
* | `retention` | `cohortEvent`, `returnEvent` (or `compounded`) | see `RetentionConfig` |
|
|
2093
|
+
* | `distinctCount` | `event`, `property` | `topN` |
|
|
2094
|
+
* | `eventBreakdown` (v1.6) | `breakdownProperty` | `event`, `countType`, `topN`, `firstTimeOnly` |
|
|
2095
|
+
* | `uniques` (v1.6) | `event` | `unit`, `rollingWindow`, `cumulative`, `countType`, `firstTimeOnly` |
|
|
2096
|
+
* | `lifecycle` (v1.6) | `valueMomentEvent` | `periodDays` (7 or 30) |
|
|
2097
|
+
* | `topPaths` (v1.6) | `anchors` | `forward`, `reverse`, `countType`, `hiddenEvents`, `visibleEvents`, `output` |
|
|
1934
2098
|
*/
|
|
1935
2099
|
export interface EmulateOptions {
|
|
1936
|
-
type: 'frequencyByFrequency' | 'funnelFrequency' | 'aggregatePerUser' | 'timeToConvert' | 'attributedBy' | 'sessionMetrics' | 'retention' | 'distinctCount';
|
|
2100
|
+
type: 'frequencyByFrequency' | 'funnelFrequency' | 'aggregatePerUser' | 'timeToConvert' | 'attributedBy' | 'sessionMetrics' | 'retention' | 'distinctCount' | 'eventBreakdown' | 'uniques' | 'lifecycle' | 'topPaths';
|
|
1937
2101
|
metricEvent?: string;
|
|
1938
2102
|
breakdownByFrequencyOf?: string;
|
|
1939
2103
|
perUser?: boolean;
|
|
@@ -1977,40 +2141,121 @@ export interface EmulateOptions {
|
|
|
1977
2141
|
* `period`; empty rows have ONLY `period` and `_empty: true`.
|
|
1978
2142
|
*/
|
|
1979
2143
|
timeBucketRange?: { from: number | string; to: number | string };
|
|
1980
|
-
/**
|
|
2144
|
+
/** sessionMetrics: which metrics to compute. Default: `['count', 'duration', 'eventsPerSession']`. (The session filter to a containing event is the `event` field.) */
|
|
1981
2145
|
metrics?: Array<'count' | 'duration' | 'eventsPerSession'>;
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2146
|
+
/**
|
|
2147
|
+
* v1.6.0 — sessionMetrics session source. `'derived'` (default) re-derives
|
|
2148
|
+
* sessions from raw timestamps via `sessionize()` — what Mixpanel actually
|
|
2149
|
+
* computes at query time. `'stamped'` restores the 1.5 reading of the
|
|
2150
|
+
* generator's pre-stamped `session_id`. Rows carry `stampedDivergence`
|
|
2151
|
+
* auditing the gap. Invalid values throw.
|
|
2152
|
+
*/
|
|
2153
|
+
source?: 'derived' | 'stamped';
|
|
2154
|
+
/** v1.6.0 — session derivation timeout in ms (default 30 min). Used by sessionMetrics, `countType: 'sessions'`, and topPaths. */
|
|
2155
|
+
sessionTimeoutMs?: number;
|
|
2156
|
+
/** v1.6.0 — max session length in ms (default 24 h). */
|
|
2157
|
+
maxSessionMs?: number;
|
|
2158
|
+
|
|
2159
|
+
// v1.5.0 retention extensions (completed in v1.6.0 — unknown option keys THROW)
|
|
2160
|
+
/** Retention birth event name (`null` or `'$any_event'` = any event). */
|
|
2161
|
+
cohortEvent?: string | null;
|
|
2162
|
+
/** Retention return event name (`null` or `'$any_event'` = any event). Forbidden with `compounded: true`. */
|
|
2163
|
+
returnEvent?: string | null;
|
|
1988
2164
|
/** Day buckets to check (offsets from birth, ≥1). */
|
|
1989
2165
|
dayBuckets?: number[];
|
|
1990
|
-
/** Segment
|
|
2166
|
+
/** Segment by event property value (birth event by default; see `segmentOn`). */
|
|
1991
2167
|
segmentBy?: string;
|
|
1992
|
-
/**
|
|
2168
|
+
/**
|
|
2169
|
+
* v1.6.0 — property filter on the birth event. Map of prop → value (shorthand
|
|
2170
|
+
* for `eq`) or `{ op, value }` with the shared WHERE rulebook ops
|
|
2171
|
+
* (`eq`/`neq`/`gt`/`lt`/`gte`/`lte`/`contains`/`not_contains`).
|
|
2172
|
+
*/
|
|
2173
|
+
cohortWhere?: Record<string, unknown>;
|
|
2174
|
+
/** v1.6.0 — property filter on the return event (same shape as `cohortWhere`). Forbidden with `compounded: true`. */
|
|
2175
|
+
returnWhere?: Record<string, unknown>;
|
|
2176
|
+
/** v1.6.0 — compounded retention: the return side is the cohort side (event AND filters — retention_query.cpp:677-685). */
|
|
2177
|
+
compounded?: boolean;
|
|
2178
|
+
/** v1.6.0 — bucket width. `month` is 31 FIXED days, not calendar months (libquery/util.h:265-273). Default `'day'`. */
|
|
2179
|
+
bucketUnit?: 'hour' | 'day' | 'week' | 'month';
|
|
2180
|
+
/** v1.6.0 — unbounded retention mode. Default `'none'`. */
|
|
2181
|
+
unbounded?: 'none' | 'carryForward' | 'carryBack' | 'consecutiveForward';
|
|
2182
|
+
/** @deprecated v1.6.0 — boolean alias for `unbounded: 'carryForward'`. Use `unbounded`. */
|
|
1993
2183
|
carry_forward?: boolean;
|
|
2184
|
+
/** v1.6.0 — birth-time alignment. `'calendarStart'` floors the birth to the bucket-unit boundary (ISO Monday for weeks). Default `'birth'`. */
|
|
2185
|
+
bucketAlignment?: 'birth' | 'calendarStart';
|
|
2186
|
+
/** v1.6.0 — restrict births to this inclusive ms/ISO window. */
|
|
2187
|
+
cohortWindow?: { from?: number | string; to?: number | string };
|
|
2188
|
+
/** v1.6.0 — which event `segmentBy` reads. `'return'` = SEGMENT_EVENT_SECOND (a user can appear in multiple segments). Default `'birth'`. */
|
|
2189
|
+
segmentOn?: 'birth' | 'return';
|
|
1994
2190
|
/**
|
|
1995
2191
|
* v1.5.0 — Mixpanel `birth_can_retain` (retention_query.cpp:1097-1109). Default
|
|
1996
2192
|
* `false`: a return event at the EXACT birth ms is NOT counted (strict `<`).
|
|
1997
|
-
* Set `true` to count exact-birth-ms returns
|
|
1998
|
-
*
|
|
2193
|
+
* Set `true` to count exact-birth-ms returns. For same-event-as-birth patterns
|
|
2194
|
+
* see `compounded: true` (supported since v1.6.0).
|
|
1999
2195
|
*/
|
|
2000
2196
|
birthCanRetain?: boolean;
|
|
2001
2197
|
|
|
2002
2198
|
// v1.5.1 distinctCount extensions
|
|
2003
|
-
/**
|
|
2199
|
+
/** distinctCount: cap on `top_values` (default 25, matches Mixpanel UI). eventBreakdown: segment cap (default 250, no "other" bucket). */
|
|
2004
2200
|
topN?: number;
|
|
2201
|
+
|
|
2202
|
+
// v1.6.0 eventBreakdown / uniques / lifecycle / topPaths extensions
|
|
2203
|
+
/** eventBreakdown + topPaths: event property to segment by (lists fan out per item; `null`/`undefined` → the "undefined" bucket). */
|
|
2204
|
+
breakdownProperty?: string;
|
|
2205
|
+
/**
|
|
2206
|
+
* Count type. eventBreakdown: `'general'` (events, default), `'unique'`
|
|
2207
|
+
* (distinct users), `'sessions'` (once per user-session per segment) —
|
|
2208
|
+
* unrecognized values throw. uniques: `'sessions'` counts distinct
|
|
2209
|
+
* (user, session) pairs per bucket. topPaths: `'unique' | 'general' | 'sessions'`.
|
|
2210
|
+
*/
|
|
2211
|
+
countType?: 'general' | 'unique' | 'sessions';
|
|
2212
|
+
/** v1.6.0 — restrict to each user's first-ever occurrence of `event` before counting (eventBreakdown + uniques). */
|
|
2213
|
+
firstTimeOnly?: boolean;
|
|
2214
|
+
/** uniques: bucket unit (default `'day'`; `'range'` = one bucket over the whole window). */
|
|
2215
|
+
unit?: 'hour' | 'day' | 'week' | 'month' | 'range';
|
|
2216
|
+
/** uniques: rolling look-back window in days (WAU=7, MAU=30). An event on day E lands the user in buckets [E, E+W−1]. */
|
|
2217
|
+
rollingWindow?: number;
|
|
2218
|
+
/** uniques: each bucket reports the running distinct-user count through that bucket. Not composable with `countType: 'sessions'`. */
|
|
2219
|
+
cumulative?: boolean;
|
|
2220
|
+
/** uniques: property filter on counted events (same shape as `cohortWhere`). */
|
|
2221
|
+
where?: Record<string, unknown>;
|
|
2222
|
+
/** lifecycle: the value-moment event classified into new/retained/resurrected/dormant. */
|
|
2223
|
+
valueMomentEvent?: string;
|
|
2224
|
+
/** lifecycle: period length — 7 or 30 (the LCA board-template variants). Default 7. */
|
|
2225
|
+
periodDays?: 7 | 30;
|
|
2226
|
+
/** topPaths: anchor event name(s) paths start from. */
|
|
2227
|
+
anchors?: string[];
|
|
2228
|
+
/** topPaths: steps captured after the anchor (default 2, ARB cap 10). */
|
|
2229
|
+
forward?: number;
|
|
2230
|
+
/** topPaths: steps captured before the anchor (default 0). */
|
|
2231
|
+
reverse?: number;
|
|
2232
|
+
/** topPaths: events removed from streams before matching. */
|
|
2233
|
+
hiddenEvents?: string[];
|
|
2234
|
+
/** topPaths: when set, ONLY these events (plus anchors) are visible. */
|
|
2235
|
+
visibleEvents?: string[];
|
|
2236
|
+
/** topPaths: collapse immediate repeats of the same event (default true). */
|
|
2237
|
+
collapseRepeated?: boolean;
|
|
2238
|
+
/** topPaths: output shape — `'list'` (ARB Top Paths leaf rows, top-50) or `'sankey'`. Default `'list'`. */
|
|
2239
|
+
output?: 'list' | 'sankey';
|
|
2240
|
+
/** topPaths: per-level top-N pruning threshold into `$mp_uncommon_flows_events`. */
|
|
2241
|
+
cardinalityThreshold?: number;
|
|
2005
2242
|
}
|
|
2006
2243
|
|
|
2007
|
-
/**
|
|
2244
|
+
/**
|
|
2245
|
+
* v1.5.0 — config for `emulateBreakdown({ type: 'retention' })`. Completed in
|
|
2246
|
+
* v1.6.0: `compounded`, `cohortWhere`/`returnWhere`, `bucketUnit`, `unbounded`
|
|
2247
|
+
* (carryForward/carryBack/consecutiveForward), `bucketAlignment: 'calendarStart'`,
|
|
2248
|
+
* `cohortWindow`, `segmentOn: 'return'`. Unknown option keys THROW (v1.6.0
|
|
2249
|
+
* behavior change — a typo'd option no longer runs silently with defaults).
|
|
2250
|
+
*/
|
|
2008
2251
|
export interface RetentionConfig extends EmulateOptions {
|
|
2009
2252
|
type: 'retention';
|
|
2010
|
-
cohortEvent: string;
|
|
2011
|
-
|
|
2012
|
-
|
|
2253
|
+
cohortEvent: string | null;
|
|
2254
|
+
/** Required unless `compounded: true` (which sets the return side := cohort side). */
|
|
2255
|
+
returnEvent?: string | null;
|
|
2256
|
+
dayBuckets?: number[];
|
|
2013
2257
|
segmentBy?: string;
|
|
2258
|
+
/** @deprecated v1.6.0 — use `unbounded: 'carryForward'`. */
|
|
2014
2259
|
carry_forward?: boolean;
|
|
2015
2260
|
}
|
|
2016
2261
|
|
|
@@ -2021,6 +2266,8 @@ export interface SessionMetricsConfig extends EmulateOptions {
|
|
|
2021
2266
|
event?: string;
|
|
2022
2267
|
/** Which metrics to compute. Default: `['count', 'duration', 'eventsPerSession']`. */
|
|
2023
2268
|
metrics?: Array<'count' | 'duration' | 'eventsPerSession'>;
|
|
2269
|
+
/** v1.6.0 — `'derived'` (default) re-derives sessions from timestamps; `'stamped'` reads pre-stamped `session_id` (the 1.5 behavior). */
|
|
2270
|
+
source?: 'derived' | 'stamped';
|
|
2024
2271
|
}
|
|
2025
2272
|
|
|
2026
2273
|
declare module '@ak--47/dungeon-master/verify' {
|
|
@@ -2040,11 +2287,30 @@ declare module '@ak--47/dungeon-master/verify' {
|
|
|
2040
2287
|
/** Greedy single-pass funnel evaluator options. */
|
|
2041
2288
|
export interface FunnelOptions {
|
|
2042
2289
|
conversionWindowMs?: number;
|
|
2290
|
+
/**
|
|
2291
|
+
* v1.6.0 — session-count conversion window (`{ unit: 'sessions', n }`,
|
|
2292
|
+
* n ≤ 12): steps must land within n sessions of step 0 (ordinal-only
|
|
2293
|
+
* per-step check). Mutually exclusive with `conversionWindowMs`.
|
|
2294
|
+
*/
|
|
2295
|
+
conversionWindow?: { unit: 'sessions'; n: number };
|
|
2043
2296
|
graceperiod?: boolean;
|
|
2044
2297
|
reentry?: boolean;
|
|
2045
2298
|
exclusionSteps?: ExclusionStep[];
|
|
2046
2299
|
trackStepProperties?: boolean | string[];
|
|
2047
|
-
|
|
2300
|
+
/**
|
|
2301
|
+
* `'totals'` returns an ARRAY of FunnelResult — one per attempt.
|
|
2302
|
+
* v1.6.0 `'sessions'` is Mixpanel's "count by Sessions" API-rewrite
|
|
2303
|
+
* preset: expands to `totals` + `woRepeat` + conversion window of 1
|
|
2304
|
+
* session; throws when combined with `reentry` or another window.
|
|
2305
|
+
*/
|
|
2306
|
+
countMode?: 'uniques' | 'totals' | 'sessions';
|
|
2307
|
+
/**
|
|
2308
|
+
* v1.6.0 — COUNT_TYPE_GENERAL_WO_REPEAT: totals counting where window
|
|
2309
|
+
* expiry is the ONLY restart (at most one attempt per window span).
|
|
2310
|
+
* Requires `countMode: 'totals'`; mutually exclusive with `reentry`
|
|
2311
|
+
* and `sessionScoped`.
|
|
2312
|
+
*/
|
|
2313
|
+
woRepeat?: boolean;
|
|
2048
2314
|
sessionScoped?: boolean;
|
|
2049
2315
|
}
|
|
2050
2316
|
/** Per-attempt funnel result. */
|