@ak--47/dungeon-master 1.5.4 → 1.6.1

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 (149) 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 +1 -1
  4. package/.claude/skills/create-project/provision.mjs +57 -10
  5. package/.claude/skills/powertools/SKILL.md +75 -0
  6. package/.claude/skills/powertools/pt.mjs +91 -0
  7. package/.claude/skills/powertools/snapshot-project.mjs +124 -0
  8. package/.claude/skills/verify-dungeon/SKILL.md +56 -25
  9. package/.claude/skills/verify-dungeon/references/counting-semantics.md +26 -6
  10. package/.claude/skills/verify-dungeon/references/report-format.md +14 -8
  11. package/.claude/skills/write-hooks/SKILL.md +156 -18
  12. package/CHANGELOG.md +224 -0
  13. package/HOOKS.md +566 -112
  14. package/README.md +30 -9
  15. package/docs/guides/1.3.0-upgrade-guide.md +262 -0
  16. package/docs/guides/1.3.1-upgrade-guide.md +328 -0
  17. package/docs/guides/1.4.0-upgrade-guide.md +130 -0
  18. package/docs/guides/1.4.1-upgrade-guide.md +125 -0
  19. package/docs/guides/1.4.3-upgrade-guide.md +200 -0
  20. package/docs/guides/1.4.4-upgrade-guide.md +95 -0
  21. package/docs/guides/1.5.0-upgrade-guide.md +426 -0
  22. package/docs/guides/1.5.1-upgrade-guide.md +178 -0
  23. package/docs/guides/1.5.3-upgrade-guide.md +117 -0
  24. package/docs/guides/1.6.0-upgrade-guide.md +204 -0
  25. package/dungeons/technical/stories-verify.js +152 -0
  26. package/dungeons/vertical/README.md +154 -0
  27. package/dungeons/vertical/ai-platform/ai-platform.js +1424 -0
  28. package/dungeons/vertical/ai-platform/ai-platform.sql +184 -0
  29. package/dungeons/vertical/ai-platform/ai-platform.verify.mjs +71 -0
  30. package/dungeons/vertical/community/community.js +1589 -0
  31. package/dungeons/vertical/community/community.sql +216 -0
  32. package/dungeons/vertical/community/community.verify.mjs +72 -0
  33. package/dungeons/vertical/crypto/crypto.js +1483 -0
  34. package/dungeons/vertical/crypto/crypto.sql +194 -0
  35. package/dungeons/vertical/crypto/crypto.verify.mjs +78 -0
  36. package/dungeons/vertical/dating/dating.js +1540 -0
  37. package/dungeons/vertical/dating/dating.sql +288 -0
  38. package/dungeons/vertical/dating/dating.verify.mjs +72 -0
  39. package/dungeons/vertical/devtools/devtools.js +1524 -0
  40. package/dungeons/vertical/devtools/devtools.sql +161 -0
  41. package/dungeons/vertical/devtools/devtools.verify.mjs +73 -0
  42. package/dungeons/vertical/ecommerce/ecommerce.js +1480 -0
  43. package/dungeons/vertical/ecommerce/ecommerce.sql +218 -0
  44. package/dungeons/vertical/ecommerce/ecommerce.verify.mjs +70 -0
  45. package/dungeons/vertical/education/education.js +1763 -0
  46. package/dungeons/vertical/education/education.sql +243 -0
  47. package/dungeons/vertical/education/education.verify.mjs +75 -0
  48. package/dungeons/vertical/fintech/fintech.js +1410 -0
  49. package/dungeons/vertical/fintech/fintech.sql +175 -0
  50. package/dungeons/vertical/fintech/fintech.verify.mjs +70 -0
  51. package/dungeons/vertical/fitness/fitness.js +1495 -0
  52. package/dungeons/vertical/fitness/fitness.sql +206 -0
  53. package/dungeons/vertical/fitness/fitness.verify.mjs +71 -0
  54. package/dungeons/vertical/food-delivery/food-delivery.js +1641 -0
  55. package/dungeons/vertical/food-delivery/food-delivery.sql +183 -0
  56. package/dungeons/vertical/food-delivery/food-delivery.verify.mjs +75 -0
  57. package/dungeons/vertical/gaming/gaming.js +1741 -0
  58. package/dungeons/vertical/gaming/gaming.sql +249 -0
  59. package/dungeons/vertical/gaming/gaming.verify.mjs +70 -0
  60. package/dungeons/vertical/healthcare/healthcare.js +1488 -0
  61. package/dungeons/vertical/healthcare/healthcare.sql +193 -0
  62. package/dungeons/vertical/healthcare/healthcare.verify.mjs +72 -0
  63. package/dungeons/vertical/insurance-application/insurance-application.js +1852 -0
  64. package/dungeons/vertical/insurance-application/insurance-application.sql +174 -0
  65. package/dungeons/vertical/insurance-application/insurance-application.verify.mjs +74 -0
  66. package/dungeons/vertical/logistics/logistics.js +1541 -0
  67. package/dungeons/vertical/logistics/logistics.sql +205 -0
  68. package/dungeons/vertical/logistics/logistics.verify.mjs +73 -0
  69. package/dungeons/vertical/marketplace/marketplace.js +1438 -0
  70. package/dungeons/vertical/marketplace/marketplace.sql +148 -0
  71. package/dungeons/vertical/marketplace/marketplace.verify.mjs +74 -0
  72. package/dungeons/vertical/{media.js → media/media.js} +557 -58
  73. package/dungeons/vertical/media/media.sql +173 -0
  74. package/dungeons/vertical/media/media.verify.mjs +74 -0
  75. package/dungeons/vertical/real-estate/real-estate.js +1629 -0
  76. package/dungeons/vertical/real-estate/real-estate.sql +167 -0
  77. package/dungeons/vertical/real-estate/real-estate.verify.mjs +75 -0
  78. package/dungeons/vertical/sass/sass.js +1733 -0
  79. package/dungeons/vertical/sass/sass.sql +173 -0
  80. package/dungeons/vertical/sass/sass.verify.mjs +74 -0
  81. package/dungeons/vertical/social/social.js +1456 -0
  82. package/dungeons/vertical/social/social.sql +160 -0
  83. package/dungeons/vertical/social/social.verify.mjs +76 -0
  84. package/dungeons/vertical/streaming/streaming.js +677 -0
  85. package/dungeons/vertical/streaming/streaming.sql +101 -0
  86. package/dungeons/vertical/streaming/streaming.verify.mjs +75 -0
  87. package/dungeons/vertical/support-desk/support-desk.js +741 -0
  88. package/dungeons/vertical/support-desk/support-desk.sql +137 -0
  89. package/dungeons/vertical/support-desk/support-desk.verify.mjs +75 -0
  90. package/dungeons/vertical/travel/travel.js +1603 -0
  91. package/dungeons/vertical/travel/travel.sql +236 -0
  92. package/dungeons/vertical/travel/travel.verify.mjs +72 -0
  93. package/index.js +7 -3
  94. package/lib/core/config-validator.js +37 -2
  95. package/lib/core/context.js +4 -2
  96. package/lib/core/dungeon-loader.js +20 -15
  97. package/lib/core/dungeon-to-json.js +22 -4
  98. package/lib/core/storage.js +3 -3
  99. package/lib/generators/funnels.js +36 -9
  100. package/lib/hook-helpers/cohort.js +53 -0
  101. package/lib/hook-helpers/index.js +8 -0
  102. package/lib/hook-helpers/mutate.js +1 -1
  103. package/lib/hook-helpers/shape.js +319 -0
  104. package/lib/hook-patterns/aggregate-per-user-by-bin.js +13 -5
  105. package/lib/hook-patterns/attributed-by-source.js +60 -58
  106. package/lib/hook-patterns/frequency-by-frequency.js +14 -4
  107. package/lib/hook-patterns/funnel-frequency-breakdown.js +24 -12
  108. package/lib/hook-patterns/index.js +1 -1
  109. package/lib/hook-patterns/time-to-convert-by-segment.js +75 -13
  110. package/lib/orchestrators/user-loop.js +41 -0
  111. package/lib/templates/story-spec.schema.json +175 -0
  112. package/lib/utils/json-evaluator.js +1 -1
  113. package/lib/utils/utils.js +151 -59
  114. package/lib/verify/coerce.js +186 -0
  115. package/lib/verify/counting.js +270 -32
  116. package/lib/verify/emulate-breakdown.js +1285 -203
  117. package/lib/verify/first-time.js +89 -0
  118. package/lib/verify/flows.js +679 -0
  119. package/lib/verify/formula.js +259 -0
  120. package/lib/verify/funnel-engine.js +697 -113
  121. package/lib/verify/index.js +32 -4
  122. package/lib/verify/sessionize.js +275 -0
  123. package/lib/verify/story-runner.js +483 -0
  124. package/lib/verify/verify-dungeon.js +59 -37
  125. package/package.json +2 -1
  126. package/scripts/extract-dungeon-schema.mjs +2 -2
  127. package/scripts/run-many.mjs +2 -2
  128. package/scripts/verify-runner.mjs +2 -2
  129. package/scripts/verify-stories.mjs +244 -0
  130. package/types.d.ts +298 -30
  131. package/dungeons/vertical/ai-platform.js +0 -818
  132. package/dungeons/vertical/community.js +0 -966
  133. package/dungeons/vertical/crypto.js +0 -895
  134. package/dungeons/vertical/dating.js +0 -830
  135. package/dungeons/vertical/devtools.js +0 -1050
  136. package/dungeons/vertical/ecommerce.js +0 -811
  137. package/dungeons/vertical/education.js +0 -964
  138. package/dungeons/vertical/fintech.js +0 -946
  139. package/dungeons/vertical/fitness.js +0 -935
  140. package/dungeons/vertical/food-delivery.js +0 -934
  141. package/dungeons/vertical/gaming.js +0 -1120
  142. package/dungeons/vertical/healthcare.js +0 -945
  143. package/dungeons/vertical/insurance-application.js +0 -1039
  144. package/dungeons/vertical/logistics.js +0 -986
  145. package/dungeons/vertical/marketplace.js +0 -1019
  146. package/dungeons/vertical/real-estate.js +0 -872
  147. package/dungeons/vertical/sass.js +0 -995
  148. package/dungeons/vertical/social.js +0 -904
  149. package/dungeons/vertical/travel.js +0 -930
package/CHANGELOG.md CHANGED
@@ -2,6 +2,230 @@
2
2
 
3
3
  All notable changes to `@ak--47/dungeon-master`.
4
4
 
5
+ ## 1.6.1 — 2026-07-08
6
+
7
+ ### Fixed
8
+
9
+ - **Unweighted string arrays now produce stable power-law distributions**
10
+ (the long-documented intent finally works). Previously `choose()` rolled a
11
+ fresh random winner on every event — two compounding bugs (`!mostChosenIndex`
12
+ discarding an explicit `0`, plus a fresh `pickAWinner` closure per event) —
13
+ so aggregate breakdowns came out uniform (±edge noise). Now any plain array
14
+ of 3–19 unique strings gets ONE seed-deterministic winner per array per run,
15
+ drawn as ~45% winner / ~25% second / ~15% third / geometric-decay tail
16
+ (`winnerWeights` in `lib/utils/utils.js` is the single tuning point).
17
+ - Existing opt-outs unchanged: arrays containing
18
+ `variant`/`group`/`experiment`/`population`, arrays with explicit
19
+ duplicate entries (dupes honored exactly), arrays of length ≤2 or ≥20,
20
+ and non-string values.
21
+ - Direct `u.pickAWinner([...])` use in dungeon files gets the same stable
22
+ memoized winner for the run; explicit `pickAWinner(arr, 0)` is honored now.
23
+ (Note: `pickAWinner` calls at module-import time in dungeon files execute
24
+ before the run's seed is applied — as before — so those winners are stable
25
+ within a run but not pinned across processes.) Each `pickAWinner` resolver
26
+ now also carries its own expansion — previously `choose()`'s resolver cache
27
+ keyed by function source could serve one property's value list to a
28
+ different property.
29
+ - UTM properties (`utm_campaign` etc.) draw through the same path, so
30
+ campaign values now skew realistically per network instead of splitting
31
+ uniformly.
32
+ - New `resetValueCaches()` clears the per-run winner memo (and the
33
+ weighted-array resolver cache, which previously leaked across in-process
34
+ runs); called automatically by `initChance()` and at every run start.
35
+ - Side effect: ~1 RNG draw per string-array property per event instead of
36
+ ~30, and the seeded RNG stream shifts vs 1.6.0 — same seed no longer
37
+ reproduces 1.6.0 output byte-for-byte (within-version determinism is
38
+ unchanged).
39
+ - **Engine-shape canary and hook-pattern integration tests made truly
40
+ deterministic** — their generating tests now run `describe.sequential`,
41
+ since concurrent in-process `generate()` calls interleave draws (and
42
+ re-seeds) on the shared seeded chance. The hook-pattern negative control
43
+ was recalibrated to 600 users; at 150 it exceeded its own threshold on
44
+ 1.6.0 too and only passed under one lucky interleaving.
45
+
46
+ ## 1.6.0 — 2026-07-04
47
+
48
+ ### Added
49
+
50
+ - **Emulator: five new analysis types + retention completion**
51
+ (`emulateBreakdown`, all ARB-cited):
52
+ - `eventBreakdown` — Insights "Total" broken down by a property, with
53
+ Mixpanel's exact segment coercion (list fan-out, `$empty_list`,
54
+ `undefined` bucket, case-sensitive type-tagged segments, topN 250);
55
+ `countType: 'unique' | 'sessions'`, `firstTimeOnly` compose;
56
+ unrecognized `countType` values throw (same strict-option rule as
57
+ retention keys).
58
+ - `uniques` — per-interval independent dedup, rolling XAU windows,
59
+ cumulative running distinct; `countType: 'sessions'`, `firstTimeOnly`.
60
+ - `lifecycle` — Lifecycle Cohort Analysis board-template classification
61
+ (new / retained / resurrected / dormant) on a value-moment event, 7- or
62
+ 30-day periods.
63
+ - `topPaths` — Flows: next-anchor-only matching, forward/reverse capacity
64
+ rings, per-level top-N pruning into `$mp_uncommon_flows_events`,
65
+ `hiddenEvents` / `visibleEvents`, `countType: 'general' | 'unique' |
66
+ 'sessions'`.
67
+ - `distinctCount` — distinct values of a property + top-N value counts.
68
+ - `retention` completion — `compounded`, `birthCanRetain`,
69
+ `carryForward` / `carryBack` / `consecutiveForward`, `calendarStart`,
70
+ `cohortWindow`, `segmentOn: 'return'`, internal-event ignore list.
71
+ - **Funnel evaluator upgrades**: session-count conversion windows,
72
+ `countMode: 'sessions'`, ARB-exact exclusion handling, any-order step
73
+ blocks, step-0-anchored trends under `timeBucket`.
74
+ - **New verify primitives**: `sessionize()` (query-time sessions — 30-min
75
+ gap / 24h max / UTC-day triggers, synthetic `$session_start`/`$session_end`),
76
+ `filterFirstTimeEver()`, `evaluateFormula()` (ARB formula grammar),
77
+ `extractFlows` / `aggregateFlows`, breakdown-key coercion
78
+ (`lib/verify/coerce.js`), `frequencyHistogram`, null-aware avg/sum
79
+ `{ flatten: true }`, `attributedBy` per-conversion output.
80
+ - **Hook atoms**: `hashCohort` (seed-stable cohort assignment),
81
+ `applyLifecycleWave`, `applyPathBias`, `applySessionShape`; pattern
82
+ `applyTTCBySegmentV2` (see Deprecated).
83
+ - **Experiments: `sticky` knob** (`ExperimentConfig.sticky`, default
84
+ `true`). Sticky bucketing — the pre-1.6 per-user hash — is now explicit
85
+ and opt-out-able: `sticky: false` re-rolls the variant on every funnel
86
+ pass via the seeded RNG. Default preserves byte-identical output for
87
+ existing dungeons.
88
+ - **Story layer**: `stories` named export on dungeons — one machine-checkable
89
+ story per hook (`DungeonStory` typedef,
90
+ `lib/templates/story-spec.schema.json`) — and the
91
+ `scripts/verify-stories.mjs` runner: mechanical five-tier verdicts
92
+ (NAILED / STRONG / WEAK / NONE / INVERSE), population floors (`minCohort`),
93
+ hook-coverage discipline, disk + in-memory modes, `--json`.
94
+ - **Verticals**: `dungeons/vertical/` restructured to one folder per vertical
95
+ (`<name>/<name>.js` + `<name>.verify.mjs` + `<name>.sql`); `stories`
96
+ exports and rebuilt hooks across all verticals; two new showcase dungeons —
97
+ `streaming` (lifecycle) and `support-desk` (flows + sessions).
98
+ - **Skills**: `/write-hooks` authors the stories export; `/verify-dungeon`
99
+ runs the story runner first and investigates only failures;
100
+ `/create-dungeon` designs analysis-friendly vocabularies (session
101
+ fan-out, value moment, hidden-event hygiene); `/analyze-soup` queries in
102
+ UTC; `/create-project` builds business context from the stories export.
103
+ - **Docs**: HOOKS.md §2.12–2.17 (event breakdown coercion, uniques/XAU,
104
+ formulas, first-time-ever, lifecycle, flows, sessions), recipes 4.29–4.31,
105
+ atom/helper reference sections.
106
+
107
+ ### Behavior changes
108
+
109
+ - **Retention option keys are strict** (P1.5). Unknown keys in a `retention`
110
+ emulator config now throw instead of being silently ignored — a typo'd
111
+ option previously ran with defaults and produced plausible-but-wrong
112
+ numbers. `carry_forward: true` is kept as a deprecated alias for
113
+ `unbounded: 'carryForward'`.
114
+ - **Funnel exclusions no longer fire before step 0** (P1.6.4).
115
+ `evaluateFunnel`'s `exclusionSteps` previously defaulted `afterStep` to
116
+ −Infinity, so an exclusion event could condemn an attempt before the first
117
+ step was ever reached. ARB has no exclusion gaps before the first step: a
118
+ pre-step-0 exclusion event now only matters inside the 2-second grace rule
119
+ at step 0 (condemns with `excludedAtStep`), otherwise the attempt proceeds.
120
+ - **Non-sequential funnel orders verify with full ARB semantics** (P1.6.6).
121
+ `first-fixed` / `last-fixed` / `first-and-last-fixed` / `outside-in` /
122
+ `random` previously verified via set-membership ("fired all step events,
123
+ any order", `verificationKind: 'partial'`); they now route through
124
+ any-order step blocks with full conversion-window / 2-second-rule /
125
+ exclusion / anchor-ordering semantics. Users that passed the loose check
126
+ but violate window or anchor ordering no longer convert. `middle-fixed`
127
+ keeps set-membership (its scrambled slots are non-contiguous).
128
+ - **`sessionMetrics` defaults to query-time derived sessions** (P1.7.2). New
129
+ `source: 'derived' | 'stamped'` option, default `'derived'`: sessions are
130
+ re-derived from raw timestamps via `sessionize()` — what Mixpanel actually
131
+ computes — instead of reading the generator's pre-stamped `session_id`.
132
+ The stamped path remains via `source: 'stamped'`, and the per-row
133
+ `stampedDivergence` count audits the gap between the two.
134
+ - **`$experiment_started` is pinned to funnel-pass start** (P4.2 engine fix,
135
+ pre-existing since 1.4.0). For experiment funnels with a non-`sequential`
136
+ `order` (`last-fixed`, `random`, `first-fixed`, ...), `applyOrderingStrategy`
137
+ shuffled the synthetic exposure event into the funnel body — the exposure
138
+ landed mid-pass at a uniform position, so exposure→conversion TTC read ~58%
139
+ of `timeToConvert`, and any exposure-anchored conversion measurement (the
140
+ Mixpanel Experiments report, ordered-funnel pairing from
141
+ `$experiment_started`) undercounted variant lift. The ordering strategy now
142
+ shuffles only the real steps; `$experiment_started` stays at execution index
143
+ 0 (offset 0), and `first-fixed`/`first-and-last-fixed` pin the true first
144
+ step instead of the exposure marker. Output changes (event order + RNG
145
+ stream) for experiment funnels with shuffle orders; `sequential` experiment
146
+ funnels are unaffected.
147
+ - **Session IDs are re-derived after the `everything` hook** (P2.1). The first
148
+ `assignSessionIds` pass still runs before hooks (hooks may read
149
+ `session_id`), but a second pass now relabels on the FINAL event set — after
150
+ the `everything` hook, auto-sort, and the future-time guard. Time-mutating
151
+ hooks (TTC scaling, injected bursts) previously left stale session ids that
152
+ disagreed with what Mixpanel derives from timestamps at query time. Session
153
+ ids hash from (user key + first event time of the session), so sessions
154
+ whose events did not move keep their exact ids. The per-session sticky-device
155
+ rewrite is NOT re-run — relabeling never mutates identity fields. Behavior
156
+ change only for dungeons whose hooks mutate event times; their stamped
157
+ `session_id` values now match query-time derivation
158
+ (`stampedDivergence === 0`).
159
+ - **Churn is now a hard activity boundary** (P2.2). `isChurnEvent` broke the
160
+ budget loop (stopping generation), but already-generated events carry
161
+ independent timestamps — uniform TimeSoup draws on the legacy path, a
162
+ shuffled active-day plan under `avgActiveDaysPerUser`/`retentionCurve` — so
163
+ churned users kept events DATED after their churn event. Churned users'
164
+ events are now truncated at the churn event's timestamp (the churn event
165
+ itself survives). Affects only dungeons using `isChurnEvent`; users who
166
+ return (`returnLikelihood` roll succeeds) are untouched. `simplest.js` has
167
+ no churn events, so the engine-shape canary and sweep are unaffected.
168
+ - **Bin-based patterns bin by distinct days by default** (P2.4).
169
+ `applyFrequencyByFrequency`, `applyFunnelFrequencyBreakdown`, and
170
+ `applyAggregateByBin` gain `binBy: 'events' | 'distinctDays'` (default
171
+ `'distinctDays'`, via `binByDistinctPeriods`). Mixpanel's frequency reports
172
+ — and the local emulator — bucket users by distinct calendar days, so the
173
+ old total-event-count axis could put a user in a different cohort than the
174
+ report bucket their data lands in, diluting engineered signal. Pass
175
+ `binBy: 'events'` to restore the pre-1.6 axis (also the right choice for
176
+ `applyFunnelFrequencyBreakdown`'s funnelEvents fallback, where one funnel
177
+ run rarely spans two days).
178
+ - **`applyAttributedBySource` rewritten to overwrite engine-stamped touches**
179
+ (P2.4, HOOKS.md recipe 4.26 as code). New opts:
180
+ `{ weights, property = 'utm_source', model = 'firstTouch'|'lastTouch'|'both' }`;
181
+ returns `{ overwritten, touches }`. The old copy-source-to-conversion
182
+ mechanism stamped fresh values, which under the v1.5 touchpoint cap land
183
+ outside Mixpanel's lookback and never move the attribution report. The
184
+ pattern now overwrites the value on the touch the chosen model reads and
185
+ never adds the property to unstamped events.
186
+
187
+ ### Changed
188
+
189
+ - **Shipped vertical dungeons: hook fixes that change generated output**
190
+ (P4.2 rebuild — same seeds, different data where noted):
191
+ - **media**: H10 applied the plan-tier factor to `watch_duration_min` in
192
+ two separate blocks — the engineered free/premium ratio compounded to
193
+ ~4.4x instead of the documented 2.09x. Single application now; the
194
+ duplicate block is deleted (no RNG-stream impact).
195
+ - **marketplace**: H9 funnel-post TTC scaling is restricted to the
196
+ Browse-to-Purchase funnel (it previously scaled all five; Buyer
197
+ Onboarding shares the search→view→cart prefix, so first-occurrence
198
+ funnel evaluation assembled chains across unscaled instances and the
199
+ engineered ratio never reached the report). H10 redesigned from a
200
+ windowed message-cohort purchase-drop to a total-message-count cohort
201
+ with property-only `offer_amount` effects.
202
+ - **sass**: H9 TTC scaling moved from one stitched whole-history
203
+ sequence (everything hook) to per-instance funnel-post gap scaling
204
+ gated on the `alert triggered` funnel — the old single scaled sequence
205
+ was diluted by the user's unscaled instances and never survived to the
206
+ funnel report.
207
+ - **crypto**: all hook day-boundary math converted from local-time dayjs
208
+ to UTC (dataset timestamps are UTC; boundaries previously shifted by
209
+ the host's UTC offset). H9 TTC scaling restricted to the onboarding
210
+ funnel (same cross-instance dilution class as marketplace). H6 churn
211
+ no longer erases a user's first 24 hours — the old absolute-day cutoff
212
+ shredded late-born users' signup/onboarding/auth events under the
213
+ growth macro.
214
+
215
+ ### Deprecated
216
+
217
+ - **`applyTTCBySegment`** (P2.4) — the funnel-post variant scales one run's
218
+ internal gaps, but Mixpanel's TTC measures the FIRST occurrence of each
219
+ step per user, so the scaling only reaches the report for `isFirstFunnel`
220
+ runs. Still functional; warns once. Use **`applyTTCBySegmentV2`** (new,
221
+ `everything` hook) — finds the greedy first sequence via
222
+ `findFirstSequence` and scales it with `scaleFunnelTTC`.
223
+ - **`Persona.churnRate`, `Persona.activeWindow`, `Persona.soupOverride`**
224
+ (P2.5) — declared config surface that was never implemented: nothing in
225
+ lib/ reads them after validation. Marked `@deprecated` in types.d.ts; the
226
+ validator warns once per process when a dungeon sets any of them. Not
227
+ removed (declared surface) and not implemented (config-shape freeze).
228
+
5
229
  ## 1.5.4 — 2026-06-04
6
230
 
7
231
  Patch. Import-phase progress now reaches `onProgress` consumers.