@ak--47/dungeon-master 1.8.0 → 1.8.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.
- package/.claude/skills/analyze-soup/SKILL.md +9 -0
- package/.claude/skills/create-dungeon/SKILL.md +35 -34
- package/.claude/skills/create-project/SKILL.md +6 -0
- package/.claude/skills/headless-build/SKILL.md +21 -11
- package/.claude/skills/powertools/SKILL.md +6 -2
- package/.claude/skills/release-check/SKILL.md +27 -2
- package/.claude/skills/verify-dungeon/SKILL.md +32 -13
- package/.claude/skills/verify-dungeon/references/alignment-contract.md +110 -0
- package/.claude/skills/verify-dungeon/references/counting-semantics.md +29 -16
- package/.claude/skills/verify-dungeon/references/report-format.md +23 -9
- package/.claude/skills/verify-dungeon/references/sql-recipes.md +135 -225
- package/.claude/skills/warehouse-metrics/SKILL.md +6 -0
- package/.claude/skills/write-hooks/SKILL.md +61 -48
- package/CHANGELOG.md +82 -0
- package/HOOKS.md +105 -47
- package/README.md +41 -1
- package/docs/guides/1.8.1-upgrade-guide.md +153 -0
- package/docs/guides/1.8.2-upgrade-guide.md +110 -0
- package/lib/generators/events.js +6 -0
- package/lib/generators/funnels.js +16 -0
- package/lib/hook-helpers/shape.js +73 -17
- package/lib/hook-patterns/attributed-by-source.js +4 -3
- package/lib/hook-patterns/funnel-frequency-breakdown.js +4 -7
- package/lib/orchestrators/user-loop.js +82 -15
- package/lib/verify/counting.js +7 -10
- package/lib/verify/emulate-breakdown.js +48 -29
- package/lib/verify/funnel-engine.js +93 -40
- package/lib/verify/identity.js +32 -9
- package/lib/verify/story-runner.js +93 -30
- package/lib/verify/verify-dungeon.js +4 -1
- package/package.json +1 -1
- package/scripts/verify-stories.mjs +3 -3
- package/types.d.ts +9 -5
|
@@ -28,8 +28,8 @@ In scope:
|
|
|
28
28
|
|
|
29
29
|
Out of scope:
|
|
30
30
|
- Schema changes (events, properties, funnels, superProps, userProps).
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
Send missing fields or enumeration changes back to `/create-dungeon` for an
|
|
32
|
+
explicitly authorized schema edit before continuing. This skill never changes schema.
|
|
33
33
|
- New top-level config knobs.
|
|
34
34
|
- Removing the `hook: function...` body to start over with a new schema.
|
|
35
35
|
|
|
@@ -39,14 +39,15 @@ off, mix shift drags the blended rate) are better architected as initial
|
|
|
39
39
|
conditions — duplicate funnels with swapped steps/props/`conversionRate`/
|
|
40
40
|
`timeToConvert`/`weight` (see the "Structural trend engineering" section in
|
|
41
41
|
`create-dungeon`). If a story reduces to structure, recommend the funnel
|
|
42
|
-
change back to the schema instead of writing a hook to fight the engine
|
|
43
|
-
the
|
|
44
|
-
|
|
42
|
+
change back to the schema instead of writing a hook to fight the engine.
|
|
43
|
+
Derive the expected effect from the knob, then verify the report against a neutral
|
|
44
|
+
control; finite budgets and competing histories can change the measured effect. Hooks are for
|
|
45
45
|
within-cohort behavior: segments doing more/less of something over time,
|
|
46
46
|
property values that differ by cohort, injected bursts, lifecycle waves.
|
|
47
47
|
|
|
48
48
|
## Reference reading
|
|
49
49
|
|
|
50
|
+
- [1.8.1 verification contract](../verify-dungeon/references/alignment-contract.md) - independent reports, neutral controls, counting boundaries, and evidence sufficiency.
|
|
50
51
|
- `lib/hook-helpers/index.js` — atoms (cohort, mutate, timing, inject,
|
|
51
52
|
identity). One file per group; full JSDoc on each atom.
|
|
52
53
|
- `lib/hook-patterns/index.js` — high-level recipes (one per Mixpanel
|
|
@@ -71,11 +72,11 @@ Hooks fire in this order per user (see `CLAUDE.md` for the canonical reference):
|
|
|
71
72
|
2. `"scd-pre"` — SCD entries created. Mutate in place; return ignored.
|
|
72
73
|
3. For each funnel: `"funnel-pre"` → `"event"` (per step) → `"funnel-post"`.
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
the
|
|
77
|
-
|
|
78
|
-
`props
|
|
75
|
+
**Usage anchors do not accumulate previous funnel TTC.** Each usage run samples
|
|
76
|
+
from its eligible window or active day after onboarding. `meta.firstEventTime`
|
|
77
|
+
is the supplied anchor, not necessarily the first emitted event time. Persona
|
|
78
|
+
and world-event modifiers apply before `funnel-pre`; the hook can mutate
|
|
79
|
+
`conversionRate`, `timeToConvert`, and `props` within engine constraints.
|
|
79
80
|
4. `"event"` — for non-funnel user events from `events[]`. Return value REPLACES the event.
|
|
80
81
|
5. `"everything"` — array of ALL the user's events. Return array to replace.
|
|
81
82
|
|
|
@@ -186,7 +187,7 @@ if (type === 'funnel-post' && meta.experiment) {
|
|
|
186
187
|
| cohort | `hashCohort(id, pct)` | Deterministic pct% cohort (0–100 scale). **Use this first for hidden cohorts** — replaces ad-hoc `charCodeAt % N`. When one dungeon needs several NON-overlapping cohorts, gate on disjoint `hashFloat(uid)` bands instead (e.g. `[0, 0.45)`, `[0.45, 0.70)`) |
|
|
187
188
|
| shape | `applyLifecycleWave(events, uid, opts)` | Dormancy window + resurrection burst for Lifecycle reports. When-to-use: the story is "users go quiet, then come back". Gap discipline: ONE stray value moment inside the window destroys the Resurrected read — size `dormantDays` to cover ≥2 whole lifecycle periods, keep the window inside the user's lifespan |
|
|
188
189
|
| shape | `applyPathBias(events, uid, opts)` | Inject a Flows path after the user's FIRST anchor occurrence. When-to-use: the story is "X% of users take this route". `share` is a 0–1 FRACTION (not `hashCohort`'s pct scale); needs ~≥0.20–0.25 to survive Sankey top-3-per-level pruning; per-step gaps clamped ≥1s so ordering survives |
|
|
189
|
-
| shape | `applySessionShape(events, uid, opts)` | Retime the
|
|
190
|
+
| shape | `applySessionShape(events, uid, opts)` | Retime the same records for session-duration/cadence stories. Pass known dataset bounds; explicit-bound mode rejects insufficient capacity before mutation. Legacy unbounded overfull layouts can merge sessions or cross dataset end. Call BEFORE `applyPathBias` so injected paths keep their own tight gaps. |
|
|
190
191
|
|
|
191
192
|
### Hook anti-patterns
|
|
192
193
|
|
|
@@ -236,8 +237,9 @@ writing a custom hook:
|
|
|
236
237
|
No special hook needed; engineer cohort behavior via `engagementDecay`,
|
|
237
238
|
`dropEventsWhere`, or per-user filtering in `everything`.
|
|
238
239
|
- **Session metrics** ("avg session has 6 events, lasts 4 minutes") — verify
|
|
239
|
-
with `emulateBreakdown({ type: 'sessionMetrics' })`.
|
|
240
|
-
`session_id
|
|
240
|
+
with `emulateBreakdown({ type: 'sessionMetrics' })`. Derive sessions from
|
|
241
|
+
timestamps; stamped `session_id` is diagnostic or explicit legacy mode.
|
|
242
|
+
Engineer with `applySessionShape`, passing known dataset bounds.
|
|
241
243
|
- **Reentry funnels** ("power users complete the funnel 3+ times") — set
|
|
242
244
|
`Funnel.reentry: true` (verifier hint). Engineer multiple completions via
|
|
243
245
|
`funnel-post` injecting cloned funnel sequences for that cohort.
|
|
@@ -254,9 +256,10 @@ writing a custom hook:
|
|
|
254
256
|
breakdown with `timeBucket: 'week'`. Engineer via temporal-windowed hooks
|
|
255
257
|
using `DATASET_START.add(N, 'days')`.
|
|
256
258
|
- **Identity-model dungeons** — when `identity.avgDevicePerUser > 0`
|
|
257
|
-
(or the deprecated `hasAnonIds: true`),
|
|
258
|
-
|
|
259
|
-
events
|
|
259
|
+
(or the deprecated `hasAnonIds: true`), pass `profiles` for profile segments.
|
|
260
|
+
For identity proof, pass an explicit `identityMap` derived from emitted valid
|
|
261
|
+
both-ID events. Profile pools alone establish no link; the compatibility helper
|
|
262
|
+
can merge users without emitted stitch evidence. See the shared contract.
|
|
260
263
|
|
|
261
264
|
**Schema-first reminder:** exclusion events must be declared in `events[]`
|
|
262
265
|
before referencing them as `Funnel.exclusionEvents` — the validator throws
|
|
@@ -270,9 +273,9 @@ emulator can re-derive.
|
|
|
270
273
|
| Pattern | Mixpanel analysis | Hook type | Caveat (HOOKS.md) |
|
|
271
274
|
|---------|-------------------|-----------|-------------------|
|
|
272
275
|
| `applyFrequencyByFrequency` | Insights — count(A) by per-user count(B) | everything | `binBy` defaults to `'distinctDays'` (v1.6) — bins match Mixpanel's per-user distinct-day counting, not raw event totals |
|
|
273
|
-
| `applyFunnelFrequencyBreakdown` | Funnels
|
|
276
|
+
| `applyFunnelFrequencyBreakdown` | Funnels - completion by per-user count(X) | funnel-post | Restrict scaling to the target funnel. Completed histories can combine competing instances; verify explicit report options and paired lift instead of assuming the per-run factor equals the report ratio. |
|
|
274
277
|
| `applyAggregateByBin` | Insights — avg(prop X) by per-user count(B) | everything | Same `binBy: 'distinctDays'` default as above |
|
|
275
|
-
| `applyTTCBySegmentV2` | Funnel TTC
|
|
278
|
+
| `applyTTCBySegmentV2` | Funnel TTC by user-property segment | everything | v1 (`applyTTCBySegment`, funnel-post) is deprecated. V2 scales `findFirstSequence`; verify the report's completed histories separately, including restart, grace, windows, and reentry. Neither helper guarantees the requested report ratio. |
|
|
276
279
|
| `applyAttributedBySource` | Conversions by Source (first/last touch) | everything | OVERWRITES the engine-stamped touch the chosen model reads; never stamps UTMs onto unstamped events (would blow the `maxTouchpointsPerUser` cap and land outside the last-10 lookback) |
|
|
277
280
|
|
|
278
281
|
Use a pattern when the trend matches its analysis 1:1. Drop down to atoms when
|
|
@@ -298,13 +301,17 @@ non-negotiable design rule learned the hard way:
|
|
|
298
301
|
visualization even though the data is there. Label-only path reads can
|
|
299
302
|
INVERT when a busier cohort glues extra visible events between path steps —
|
|
300
303
|
assert the share on the cohort you engineered, not globally.
|
|
304
|
+
The helper is append-only: `share` selects injection recipients, not exact
|
|
305
|
+
visible branch share. Keep competing events and measure paired branch lift.
|
|
301
306
|
- **`session-shape`** (`applySessionShape`) — 30-MIN STRADDLING + MIDNIGHT
|
|
302
|
-
RULE.
|
|
303
|
-
UTC midnight. Engineered cadences must keep intra-session gaps clearly
|
|
307
|
+
RULE. The local default derives sessions with a 30-min idle timeout and splits
|
|
308
|
+
at UTC midnight, with a 24-hour maximum. Engineered cadences must keep intra-session gaps clearly
|
|
304
309
|
UNDER 30min and inter-session gaps clearly OVER it — a gap that straddles
|
|
305
310
|
the timeout makes session counts jitter across runs. Never let an
|
|
306
311
|
engineered session cross UTC midnight (the day split cuts it in two). The
|
|
307
|
-
|
|
312
|
+
bounded helper checks placement capacity before mutation. Legacy unbounded
|
|
313
|
+
overfull requests can merge sessions. Pass known bounds and measure derived
|
|
314
|
+
sessions; do not infer dataset end from the last observed event.
|
|
308
315
|
- **`composition-drift`** — the breakdown's SHARE of a segment moves over
|
|
309
316
|
time while totals stay flat (e.g. plan-mix shifts toward premium). Engineer
|
|
310
317
|
by flipping an existing property value on a date-gated cohort, never by
|
|
@@ -339,9 +346,9 @@ events.push(clone); // ✅ inject from template
|
|
|
339
346
|
return events.filter(e => !shouldDrop(e)); // ✅ filter inside `everything`
|
|
340
347
|
```
|
|
341
348
|
|
|
342
|
-
If a trend
|
|
343
|
-
|
|
344
|
-
|
|
349
|
+
If a trend needs an undeclared property, stop that pattern and hand the schema
|
|
350
|
+
request to `/create-dungeon`. Resume only after an authorized schema edit declares
|
|
351
|
+
the default. Do not add the field or change its enumeration in this skill.
|
|
345
352
|
|
|
346
353
|
## Identity-aware hook patterns
|
|
347
354
|
|
|
@@ -463,8 +470,9 @@ Rules:
|
|
|
463
470
|
- `floor` must itself be derived (e.g. `target * 0.8`) — never hand-tuned to
|
|
464
471
|
a run. A missed assertion means fixing the hook or the derivation, never
|
|
465
472
|
relaxing the number to match output.
|
|
466
|
-
- Set `minCohort` from the
|
|
467
|
-
|
|
473
|
+
- Set `minCohort` from the planned eligible population, then inspect the actual
|
|
474
|
+
selected-row population fields and independent users/converters. The guard
|
|
475
|
+
does not prove every denominator. Report insufficient evidence separately.
|
|
468
476
|
- One story per pattern; story `hook` matches the doc-block numbering (`H3`).
|
|
469
477
|
- The `assert` function escape hatch is discouraged — each use needs a
|
|
470
478
|
comment saying why the declarative `expect` grammar can't express it.
|
|
@@ -474,12 +482,12 @@ Rules:
|
|
|
474
482
|
|
|
475
483
|
The order of operations inside the everything hook matters when hooks interact:
|
|
476
484
|
|
|
477
|
-
1. **
|
|
485
|
+
1. **Profile projection** - prefer schema-declared `stickyEventProps`; preserve projected values in clones
|
|
478
486
|
2. **Temporal value mutations that DON'T need cloned events** — e.g., version stamping
|
|
479
487
|
3. **Behavioral detection + event cloning** — agentic detection, KYC clones, pro clones, magic number clones
|
|
480
488
|
4. **Event filtering** — churn, retention, rate-limit drops
|
|
481
|
-
5. **Temporal value mutations that NEED cloned events**
|
|
482
|
-
6. **
|
|
489
|
+
5. **Temporal value mutations that NEED cloned events** - e.g., spring price boost, gas spike, outage errors (last mutation)
|
|
490
|
+
6. **Return** - the engine auto-sorts after `everything` by default; no manual output sort
|
|
483
491
|
|
|
484
492
|
**Why:** If a temporal mutation runs before cloning, cloned events that land in
|
|
485
493
|
the temporal window miss the mutation. Moving temporal value mutations to the
|
|
@@ -489,7 +497,8 @@ end ensures ALL events in the window — original and cloned — receive the eff
|
|
|
489
497
|
|
|
490
498
|
When a dungeon relied on deprecated config blocks (`subscription`, `attribution`,
|
|
491
499
|
`features`, `geo`, `anomalies`) for properties that hooks depend on, those
|
|
492
|
-
properties no longer appear in the data.
|
|
500
|
+
properties no longer appear in the data. Request an authorized schema migration
|
|
501
|
+
through `/create-dungeon` first:
|
|
493
502
|
|
|
494
503
|
1. Add the property to `superProps` and `userProps` with default values
|
|
495
504
|
2. Assign meaningful values in the `user` hook (based on hash, persona, or profile)
|
|
@@ -506,8 +515,8 @@ control group, but not so broad they catch everyone:
|
|
|
506
515
|
| Detection | Problem | Fix |
|
|
507
516
|
|-----------|---------|-----|
|
|
508
517
|
| `events.some(e => e.event === X)` with common X | 90%+ of users qualify | Require 3+ events: `events.filter(...).length >= 3` |
|
|
509
|
-
| `charCodeAt(0) % 50 === 0` |
|
|
510
|
-
| `profile.tier === "premium"` | Fixed by config distribution |
|
|
518
|
+
| `charCodeAt(0) % 50 === 0` | First-character distributions are biased; modulo does not imply 2% | Use `hashCohort(uid, 2)` and measure the realized population |
|
|
519
|
+
| `profile.tier === "premium"` | Fixed by config distribution | Request a schema distribution change if cohort is too small |
|
|
511
520
|
| `earlyEvents.length >= 5` for a low-weight event | 0% qualify (impossible threshold) | Check actual distribution first, set at ~80th percentile |
|
|
512
521
|
|
|
513
522
|
Target: 10-30% of users in the affected cohort for clean signal at 10K users.
|
|
@@ -546,11 +555,12 @@ Reserve drops for a single churn/retention effect per dungeon.
|
|
|
546
555
|
|
|
547
556
|
Apply these BEFORE handing off to `/verify-dungeon`. See HOOKS.md §9 for full recipes.
|
|
548
557
|
|
|
549
|
-
###
|
|
558
|
+
### Opt out of strict events only when standalone occurrences are required
|
|
550
559
|
|
|
551
560
|
If your hook reads `event === 'X'` and `X` is also a funnel-step event, the
|
|
552
561
|
validator auto-promotes it to `isStrictEvent: true` and the engine
|
|
553
|
-
won't emit standalone occurrences.
|
|
562
|
+
won't emit standalone occurrences. Funnel-generated occurrences remain readable;
|
|
563
|
+
only a cohort that depends on additional standalone traffic needs the opt-out.
|
|
554
564
|
|
|
555
565
|
```js
|
|
556
566
|
// BAD — login is a funnel step + read by hook
|
|
@@ -559,16 +569,15 @@ events: [{ event: 'login', weight: 4, properties: {...} }]
|
|
|
559
569
|
events: [{ event: 'login', weight: 4, isStrictEvent: false, properties: {...} }]
|
|
560
570
|
```
|
|
561
571
|
|
|
562
|
-
Audit
|
|
563
|
-
|
|
572
|
+
Audit whether the cohort needs standalone occurrences. Request any required
|
|
573
|
+
`isStrictEvent: false` schema edit through `/create-dungeon`; do not change it here.
|
|
564
574
|
|
|
565
575
|
### Reentry on per-instance loops
|
|
566
576
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
funnel.
|
|
577
|
+
**`reentry` is verifier-only.** Usage volume, funnel selection, and the event
|
|
578
|
+
budget control generated repetitions. Set `reentry: true` for a report that
|
|
579
|
+
counts repeated histories; it does not generate loops. Local totals also
|
|
580
|
+
default to `reentry: false`, so preserve the report's explicit counting options.
|
|
572
581
|
|
|
573
582
|
### Hash-based cohorts produce textbook signals
|
|
574
583
|
|
|
@@ -577,12 +586,13 @@ deterministically:
|
|
|
577
586
|
|
|
578
587
|
```js
|
|
579
588
|
// 2% whales with 50x trade amount → long-tail Insights distribution
|
|
580
|
-
const isWhale = uid
|
|
589
|
+
const isWhale = hashCohort(uid, 2);
|
|
581
590
|
if (isWhale && e.event === 'swap') e.trade_amount_usd *= 50;
|
|
582
591
|
```
|
|
583
592
|
|
|
584
593
|
Use a large multiplier (≥10x, ideally 50x) so the signal beats soup noise.
|
|
585
|
-
Use
|
|
594
|
+
Use `hashCohort(uid, 2)`, `hashCohort(uid, 4)`, or `hashCohort(uid, 10)` for
|
|
595
|
+
those target percentages, then check realized eligible cohort sizes.
|
|
586
596
|
|
|
587
597
|
### Hook ordering inside `everything`
|
|
588
598
|
|
|
@@ -604,13 +614,13 @@ counts even after reduction. Either:
|
|
|
604
614
|
```js
|
|
605
615
|
// BAD — profile.level isn't in userProps; resolves to undefined
|
|
606
616
|
if (meta.profile.level >= 50) e.gold_earned *= 3;
|
|
607
|
-
//
|
|
617
|
+
// Request a schema declaration before targeting this profile segment.
|
|
608
618
|
```
|
|
609
619
|
|
|
610
620
|
When the hook references a missing profile field, you can still get the
|
|
611
621
|
data spread you want (gold range), but the cohort can't be analytically
|
|
612
|
-
recovered.
|
|
613
|
-
cohort.
|
|
622
|
+
recovered. Request the missing userProp through `/create-dungeon`, or rewrite
|
|
623
|
+
the hook to use a hash cohort without changing schema.
|
|
614
624
|
|
|
615
625
|
## Workflow
|
|
616
626
|
|
|
@@ -648,11 +658,14 @@ cohort.
|
|
|
648
658
|
If verify-dungeon returns WEAK, NONE, or INVERSE on any pattern at full
|
|
649
659
|
fidelity, return to step 4 and refine — fix the hook or the derivation,
|
|
650
660
|
never relax a threshold to match output. Iterate until all patterns score
|
|
651
|
-
|
|
661
|
+
STRONG or NAILED with report semantics, neutral controls, and sufficient
|
|
662
|
+
independent populations verified. Insufficient evidence requires more evidence,
|
|
663
|
+
not an automatic hook rewrite or a weaker target.
|
|
652
664
|
|
|
653
665
|
## Stopping condition
|
|
654
666
|
|
|
655
|
-
Stop after `/verify-dungeon` reports all engineered patterns as STRONG or NAILED
|
|
667
|
+
Stop after `/verify-dungeon` reports all engineered patterns as STRONG or NAILED
|
|
668
|
+
and the shared proof contract is satisfied,
|
|
656
669
|
OR after three iterations without convergence — at that point, document what's
|
|
657
670
|
still off in the dungeon's overview comment and report the gap to the user.
|
|
658
671
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,88 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@ak--47/dungeon-master`.
|
|
4
4
|
|
|
5
|
+
## 1.8.2 - 2026-09-13
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Build automatic verifier identity maps from emitted both-ID events. Profile
|
|
10
|
+
device pools no longer create links by themselves. Explicit identity maps
|
|
11
|
+
retain their existing behavior; conflicting authenticated events retain their user.
|
|
12
|
+
- Pass supported held-property session funnels through the report wrapper. Keep
|
|
13
|
+
implicit one-session windows when applying matching dungeon funnel defaults.
|
|
14
|
+
- Expand list-valued held properties into separate funnel histories, including
|
|
15
|
+
repeated cursor visits and full-stream session boundaries.
|
|
16
|
+
- Preserve prior attribution touches across time buckets, ignore unstamped
|
|
17
|
+
touches, and retain conversions with no matching touch in the unknown segment.
|
|
18
|
+
- Make `minCohort` use conservative independent-user evidence. Repeated period
|
|
19
|
+
rows cannot inflate populations, and custom assertions receive the same guard.
|
|
20
|
+
- Include declared global properties on synthetic experiment exposure events,
|
|
21
|
+
so run and segment filters retain both exposures and outcomes.
|
|
22
|
+
- Make funnel-frequency drop decisions independent of random insertion IDs.
|
|
23
|
+
|
|
24
|
+
### Verified
|
|
25
|
+
|
|
26
|
+
- Add opt-in live comparisons through the actual importer and Mixpanel query
|
|
27
|
+
layer, with run-isolated identities, import budgets, retained query payloads,
|
|
28
|
+
exact local/live comparisons, fixed seeds, and neutral controls.
|
|
29
|
+
- Verify selected generated conversion, TTC, retention, volume, weight, incident,
|
|
30
|
+
frequency, aggregate, attribution, session, and Flows effects against live reports.
|
|
31
|
+
- Separate ingestion readiness from import acceptance. Historical identity links
|
|
32
|
+
can become visible later; one retained fixture converged without another import.
|
|
33
|
+
|
|
34
|
+
### Compatibility
|
|
35
|
+
|
|
36
|
+
Public exports, signatures, options, and defaults remain unchanged. Corrected
|
|
37
|
+
counts and seeded hook outcomes can differ from 1.8.1. Explicit profile-map
|
|
38
|
+
overrides and totals' `reentry: false` default remain supported. Calendar-day
|
|
39
|
+
activity, rolling frequency, and raw event-count histograms are separate reports.
|
|
40
|
+
Finite-lookback attribution, ambiguous tied touches, non-UTC project settings,
|
|
41
|
+
and arbitrary combinations remain subject to the documented limits.
|
|
42
|
+
|
|
43
|
+
See the [upgrade guide](docs/guides/1.8.2-upgrade-guide.md) and the repository's
|
|
44
|
+
live alignment report for measured scope. This entry does not indicate npm publication.
|
|
45
|
+
|
|
46
|
+
## 1.8.1 — 2026-09-12
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Align funnel counting with source-derived Mixpanel contracts: shared first/last
|
|
51
|
+
steps finalize correctly with or without reentry, completion grace consumes
|
|
52
|
+
events through the inclusive two-second boundary, and first/last-touch
|
|
53
|
+
breakdowns merge properties across reached steps.
|
|
54
|
+
- Derive session boundaries from the full event stream before hold-property-constant
|
|
55
|
+
partitioning, so unrelated activity can keep a session open.
|
|
56
|
+
- Keep onboarding after user creation and usage after onboarding. Preserve retry
|
|
57
|
+
entries when strict event-count capacity allows. Short windows retain partial
|
|
58
|
+
output and report capacity limits through existing warnings.
|
|
59
|
+
- Reconcile engine-generated identity against surviving auth events, including
|
|
60
|
+
engine-created duplicates and amplification clones. Preserve explicit hook
|
|
61
|
+
overrides and the synthetic experiment identity exception.
|
|
62
|
+
- Select the earliest chronological anchor for append-only path injection.
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- Optional `datasetStart` and `datasetEnd` bounds for `applySessionShape`, preventing
|
|
67
|
+
retimed events from being clipped outside a known dataset window. Explicit bounds
|
|
68
|
+
reject impossible session layouts before mutation; calls without bounds retain
|
|
69
|
+
legacy full-day placement.
|
|
70
|
+
- An offline `tests/alignment` family with mixed-dungeon trend checks, neutral
|
|
71
|
+
controls, multiple seeds, sample-size guards, counting contracts, and a diagnostic
|
|
72
|
+
sweep with a hard ten-minute deadline. The runner denies network access at the OS
|
|
73
|
+
level on macOS and fails closed elsewhere.
|
|
74
|
+
- A control inventory, failure-first repair evidence, and measured operating ranges.
|
|
75
|
+
Release validation passed 175 alignment tests and 2,022 unit/integration tests, with
|
|
76
|
+
one existing skip. The 297-cell sweep generated 17.08 million events cumulatively:
|
|
77
|
+
125 cells met their evidence thresholds and 172 had insufficient evidence.
|
|
78
|
+
|
|
79
|
+
### Compatibility
|
|
80
|
+
|
|
81
|
+
Existing call forms and defaults remain supported. Session bounds are additive,
|
|
82
|
+
not required. Corrected timestamps, identities, and counts can change generated
|
|
83
|
+
output relative to 1.8.0. Totals counting still requires explicit `reentry: true`
|
|
84
|
+
for repeated histories. These tests use local Mixpanel source-derived contracts,
|
|
85
|
+
not live engine execution, and do not establish complete coverage of every knob.
|
|
86
|
+
|
|
5
87
|
## 1.8.0 — 2026-09-10
|
|
6
88
|
|
|
7
89
|
### Added — `standaloneEvents`: identity-less metric snapshots
|