@ak--47/dungeon-master 1.5.3 → 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.
Files changed (146) 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 +96 -0
  4. package/.claude/skills/create-project/provision.mjs +336 -0
  5. package/.claude/skills/verify-dungeon/SKILL.md +56 -25
  6. package/.claude/skills/verify-dungeon/references/counting-semantics.md +26 -6
  7. package/.claude/skills/verify-dungeon/references/report-format.md +14 -8
  8. package/.claude/skills/write-hooks/SKILL.md +156 -18
  9. package/CHANGELOG.md +211 -0
  10. package/HOOKS.md +566 -112
  11. package/README.md +28 -9
  12. package/docs/guides/1.3.0-upgrade-guide.md +262 -0
  13. package/docs/guides/1.3.1-upgrade-guide.md +328 -0
  14. package/docs/guides/1.4.0-upgrade-guide.md +130 -0
  15. package/docs/guides/1.4.1-upgrade-guide.md +125 -0
  16. package/docs/guides/1.4.3-upgrade-guide.md +200 -0
  17. package/docs/guides/1.4.4-upgrade-guide.md +95 -0
  18. package/docs/guides/1.5.0-upgrade-guide.md +426 -0
  19. package/docs/guides/1.5.1-upgrade-guide.md +178 -0
  20. package/docs/guides/1.5.3-upgrade-guide.md +117 -0
  21. package/docs/guides/1.6.0-upgrade-guide.md +204 -0
  22. package/dungeons/technical/stories-verify.js +152 -0
  23. package/dungeons/vertical/README.md +154 -0
  24. package/dungeons/vertical/ai-platform/ai-platform.js +1424 -0
  25. package/dungeons/vertical/ai-platform/ai-platform.sql +184 -0
  26. package/dungeons/vertical/ai-platform/ai-platform.verify.mjs +71 -0
  27. package/dungeons/vertical/community/community.js +1589 -0
  28. package/dungeons/vertical/community/community.sql +216 -0
  29. package/dungeons/vertical/community/community.verify.mjs +72 -0
  30. package/dungeons/vertical/crypto/crypto.js +1483 -0
  31. package/dungeons/vertical/crypto/crypto.sql +194 -0
  32. package/dungeons/vertical/crypto/crypto.verify.mjs +78 -0
  33. package/dungeons/vertical/dating/dating.js +1540 -0
  34. package/dungeons/vertical/dating/dating.sql +288 -0
  35. package/dungeons/vertical/dating/dating.verify.mjs +72 -0
  36. package/dungeons/vertical/devtools/devtools.js +1524 -0
  37. package/dungeons/vertical/devtools/devtools.sql +161 -0
  38. package/dungeons/vertical/devtools/devtools.verify.mjs +73 -0
  39. package/dungeons/vertical/ecommerce/ecommerce.js +1480 -0
  40. package/dungeons/vertical/ecommerce/ecommerce.sql +218 -0
  41. package/dungeons/vertical/ecommerce/ecommerce.verify.mjs +70 -0
  42. package/dungeons/vertical/education/education.js +1763 -0
  43. package/dungeons/vertical/education/education.sql +243 -0
  44. package/dungeons/vertical/education/education.verify.mjs +75 -0
  45. package/dungeons/vertical/fintech/fintech.js +1410 -0
  46. package/dungeons/vertical/fintech/fintech.sql +175 -0
  47. package/dungeons/vertical/fintech/fintech.verify.mjs +70 -0
  48. package/dungeons/vertical/fitness/fitness.js +1495 -0
  49. package/dungeons/vertical/fitness/fitness.sql +206 -0
  50. package/dungeons/vertical/fitness/fitness.verify.mjs +71 -0
  51. package/dungeons/vertical/food-delivery/food-delivery.js +1641 -0
  52. package/dungeons/vertical/food-delivery/food-delivery.sql +183 -0
  53. package/dungeons/vertical/food-delivery/food-delivery.verify.mjs +75 -0
  54. package/dungeons/vertical/gaming/gaming.js +1741 -0
  55. package/dungeons/vertical/gaming/gaming.sql +249 -0
  56. package/dungeons/vertical/gaming/gaming.verify.mjs +70 -0
  57. package/dungeons/vertical/healthcare/healthcare.js +1488 -0
  58. package/dungeons/vertical/healthcare/healthcare.sql +193 -0
  59. package/dungeons/vertical/healthcare/healthcare.verify.mjs +72 -0
  60. package/dungeons/vertical/insurance-application/insurance-application.js +1852 -0
  61. package/dungeons/vertical/insurance-application/insurance-application.sql +174 -0
  62. package/dungeons/vertical/insurance-application/insurance-application.verify.mjs +74 -0
  63. package/dungeons/vertical/logistics/logistics.js +1541 -0
  64. package/dungeons/vertical/logistics/logistics.sql +205 -0
  65. package/dungeons/vertical/logistics/logistics.verify.mjs +73 -0
  66. package/dungeons/vertical/marketplace/marketplace.js +1438 -0
  67. package/dungeons/vertical/marketplace/marketplace.sql +148 -0
  68. package/dungeons/vertical/marketplace/marketplace.verify.mjs +74 -0
  69. package/dungeons/vertical/{media.js → media/media.js} +557 -58
  70. package/dungeons/vertical/media/media.sql +173 -0
  71. package/dungeons/vertical/media/media.verify.mjs +74 -0
  72. package/dungeons/vertical/real-estate/real-estate.js +1629 -0
  73. package/dungeons/vertical/real-estate/real-estate.sql +167 -0
  74. package/dungeons/vertical/real-estate/real-estate.verify.mjs +75 -0
  75. package/dungeons/vertical/sass/sass.js +1733 -0
  76. package/dungeons/vertical/sass/sass.sql +173 -0
  77. package/dungeons/vertical/sass/sass.verify.mjs +74 -0
  78. package/dungeons/vertical/social/social.js +1456 -0
  79. package/dungeons/vertical/social/social.sql +160 -0
  80. package/dungeons/vertical/social/social.verify.mjs +76 -0
  81. package/dungeons/vertical/streaming/streaming.js +677 -0
  82. package/dungeons/vertical/streaming/streaming.sql +101 -0
  83. package/dungeons/vertical/streaming/streaming.verify.mjs +75 -0
  84. package/dungeons/vertical/support-desk/support-desk.js +741 -0
  85. package/dungeons/vertical/support-desk/support-desk.sql +137 -0
  86. package/dungeons/vertical/support-desk/support-desk.verify.mjs +75 -0
  87. package/dungeons/vertical/travel/travel.js +1603 -0
  88. package/dungeons/vertical/travel/travel.sql +236 -0
  89. package/dungeons/vertical/travel/travel.verify.mjs +72 -0
  90. package/index.js +2 -2
  91. package/lib/core/config-validator.js +37 -2
  92. package/lib/core/context.js +4 -2
  93. package/lib/core/dungeon-loader.js +20 -15
  94. package/lib/core/dungeon-to-json.js +22 -4
  95. package/lib/core/storage.js +3 -3
  96. package/lib/generators/funnels.js +36 -9
  97. package/lib/hook-helpers/cohort.js +53 -0
  98. package/lib/hook-helpers/index.js +8 -0
  99. package/lib/hook-helpers/mutate.js +1 -1
  100. package/lib/hook-helpers/shape.js +319 -0
  101. package/lib/hook-patterns/aggregate-per-user-by-bin.js +13 -5
  102. package/lib/hook-patterns/attributed-by-source.js +60 -58
  103. package/lib/hook-patterns/frequency-by-frequency.js +14 -4
  104. package/lib/hook-patterns/funnel-frequency-breakdown.js +24 -12
  105. package/lib/hook-patterns/index.js +1 -1
  106. package/lib/hook-patterns/time-to-convert-by-segment.js +75 -13
  107. package/lib/orchestrators/user-loop.js +41 -0
  108. package/lib/templates/story-spec.schema.json +175 -0
  109. package/lib/utils/json-evaluator.js +1 -1
  110. package/lib/utils/utils.js +5 -4
  111. package/lib/verify/coerce.js +186 -0
  112. package/lib/verify/counting.js +270 -32
  113. package/lib/verify/emulate-breakdown.js +1285 -203
  114. package/lib/verify/first-time.js +89 -0
  115. package/lib/verify/flows.js +679 -0
  116. package/lib/verify/formula.js +259 -0
  117. package/lib/verify/funnel-engine.js +697 -113
  118. package/lib/verify/index.js +32 -4
  119. package/lib/verify/sessionize.js +275 -0
  120. package/lib/verify/story-runner.js +483 -0
  121. package/lib/verify/verify-dungeon.js +59 -37
  122. package/package.json +3 -2
  123. package/scripts/extract-dungeon-schema.mjs +2 -2
  124. package/scripts/run-many.mjs +2 -2
  125. package/scripts/verify-runner.mjs +2 -2
  126. package/scripts/verify-stories.mjs +244 -0
  127. package/types.d.ts +296 -30
  128. package/dungeons/vertical/ai-platform.js +0 -818
  129. package/dungeons/vertical/community.js +0 -966
  130. package/dungeons/vertical/crypto.js +0 -895
  131. package/dungeons/vertical/dating.js +0 -830
  132. package/dungeons/vertical/devtools.js +0 -1050
  133. package/dungeons/vertical/ecommerce.js +0 -811
  134. package/dungeons/vertical/education.js +0 -964
  135. package/dungeons/vertical/fintech.js +0 -946
  136. package/dungeons/vertical/fitness.js +0 -935
  137. package/dungeons/vertical/food-delivery.js +0 -934
  138. package/dungeons/vertical/gaming.js +0 -1120
  139. package/dungeons/vertical/healthcare.js +0 -945
  140. package/dungeons/vertical/insurance-application.js +0 -1039
  141. package/dungeons/vertical/logistics.js +0 -986
  142. package/dungeons/vertical/marketplace.js +0 -1019
  143. package/dungeons/vertical/real-estate.js +0 -872
  144. package/dungeons/vertical/sass.js +0 -995
  145. package/dungeons/vertical/social.js +0 -904
  146. package/dungeons/vertical/travel.js +0 -930
@@ -0,0 +1,101 @@
1
+ -- ============================================================
2
+ -- streaming.js — v1.6.0 Hook Inspection Queries (DuckDB)
3
+ -- Score: see README — machine contract in streaming.js `stories`
4
+ -- Run after: node scripts/verify-runner.mjs dungeons/vertical/streaming/streaming.js verify-streaming
5
+ -- ============================================================
6
+ --
7
+ -- v1.6.0 derivation notes (2K reduced run vs organic counterfactual):
8
+ -- 1. This dungeon is the LIFECYCLE SHOWCASE: three applyLifecycleWave
9
+ -- cohorts on disjoint hashFloat bands (25% / 15% / 12%), gated to
10
+ -- early-born users so birth-relative windows land on near-absolute
11
+ -- calendar days. hashFloat is not expressible in SQL — cohort-exact
12
+ -- reads live in the stories export; these queries eyeball the same
13
+ -- signals through calendar aggregates and the whole-stream gap audit.
14
+ -- 2. Machine verification reads lifecycle TILES BY INDEX via the
15
+ -- emulator (22 seven-day tiles, 6 thirty-day tiles over the fixed
16
+ -- 151-day span). The dates below match the configured window
17
+ -- 2026-01-01 → 2026-05-31.
18
+ -- 3. Only H3 (dropAll) creates ≥25-day whole-stream gaps — H1/H2 users
19
+ -- keep browsing through their windows — so the LAG gap scan isolates
20
+ -- the campaign cohort without hashFloat.
21
+ -- ============================================================
22
+
23
+
24
+ -- Hook 1: WEEKLY LIFECYCLE WAVE — ~25% of early-born users stop playing
25
+ -- for 3 weeks (days 42-63), then burst back.
26
+ -- Expect: weekly distinct players dip ~Feb 15 - Mar 7, spike the week of
27
+ -- Mar 8 (the 4-clone resurrection burst), flat elsewhere.
28
+ SELECT DATE_TRUNC('week', time::TIMESTAMP) AS week,
29
+ COUNT(DISTINCT user_id) AS players,
30
+ COUNT(*) AS plays
31
+ FROM read_json_auto('data/verify-streaming-EVENTS*.json', sample_size=-1, union_by_name=true)
32
+ WHERE event = 'content played'
33
+ GROUP BY 1 ORDER BY 1;
34
+
35
+
36
+ -- Hook 2: MONTHLY LIFECYCLE WAVE — ~15% of early-born users go
37
+ -- value-moment-dark for 65 days (days 21-86; the gap covers all of
38
+ -- February, one whole 30d tile).
39
+ -- Expect: February's distinct-player count drops vs January/April; the
40
+ -- users missing in February reappear in late March.
41
+ SELECT DATE_TRUNC('month', time::TIMESTAMP) AS month,
42
+ COUNT(DISTINCT user_id) AS players,
43
+ COUNT(*) AS plays
44
+ FROM read_json_auto('data/verify-streaming-EVENTS*.json', sample_size=-1, union_by_name=true)
45
+ WHERE event = 'content played'
46
+ GROUP BY 1 ORDER BY 1;
47
+
48
+
49
+ -- Hook 3: RESURRECTION CAMPAIGN — ~12% of early-born users vanish
50
+ -- entirely for 30 days (days 100-130, dropAll), then return with a
51
+ -- push_open burst 15-45 min before the first post-gap play.
52
+ -- Expect: ~10% of users have a ≥25d whole-stream gap; ~all return to the
53
+ -- value moment; pushed share ~0.97 (organic: a handful of users, ~0.5).
54
+ WITH e AS (
55
+ SELECT user_id::VARCHAR AS uid, event, time::TIMESTAMP AS t,
56
+ LAG(time::TIMESTAMP) OVER (PARTITION BY user_id::VARCHAR ORDER BY time::TIMESTAMP) AS prev_t
57
+ FROM read_json_auto('data/verify-streaming-EVENTS*.json', sample_size=-1, union_by_name=true)
58
+ WHERE user_id IS NOT NULL
59
+ ), gaps AS (
60
+ SELECT uid, MAX(t - prev_t) AS max_gap FROM e WHERE prev_t IS NOT NULL GROUP BY 1
61
+ ), gap_users AS (
62
+ SELECT uid FROM gaps WHERE max_gap >= INTERVAL 25 DAY
63
+ ), gap_edge AS (
64
+ SELECT e.uid, MIN(e.t) AS gap_end
65
+ FROM e JOIN gap_users g ON e.uid = g.uid
66
+ WHERE e.prev_t IS NOT NULL AND e.t - e.prev_t >= INTERVAL 25 DAY
67
+ GROUP BY 1
68
+ ), first_play AS (
69
+ SELECT e.uid, MIN(e.t) AS play_t
70
+ FROM e JOIN gap_edge g ON e.uid = g.uid
71
+ WHERE e.event = 'content played' AND e.t >= g.gap_end
72
+ GROUP BY 1
73
+ ), pushed AS (
74
+ SELECT DISTINCT f.uid
75
+ FROM first_play f JOIN e ON e.uid = f.uid
76
+ WHERE e.event = 'push_open' AND e.t <= f.play_t AND e.t >= f.play_t - INTERVAL 24 HOUR
77
+ )
78
+ SELECT (SELECT COUNT(*) FROM gap_users) AS gap_users,
79
+ (SELECT COUNT(*) FROM first_play) AS returners,
80
+ (SELECT COUNT(*) FROM pushed) AS pushed_returners,
81
+ ROUND((SELECT COUNT(*) FROM pushed)::DOUBLE / GREATEST((SELECT COUNT(*) FROM first_play), 1), 3) AS pushed_share;
82
+
83
+
84
+ -- Hook 4 (combined effect): WAU DOUBLE TROUGH — daily distinct players,
85
+ -- bucketed. Expect: deep trough days 49-63 (H1+H2 overlap, ~0.59x
86
+ -- baseline), partial recovery, second dip days 107-137 (H3, ~0.89x),
87
+ -- full tail recovery. Eyeball the daily series for the two-dip shape.
88
+ SELECT DATE_TRUNC('day', time::TIMESTAMP) AS day,
89
+ COUNT(DISTINCT user_id) AS players
90
+ FROM read_json_auto('data/verify-streaming-EVENTS*.json', sample_size=-1, union_by_name=true)
91
+ WHERE event = 'content played'
92
+ GROUP BY 1 ORDER BY 1;
93
+
94
+
95
+ -- Identity invariants: every event carries user_id; ~all carry device_id
96
+ -- (avgDevicePerUser: 2 → devices/user ~2.1).
97
+ SELECT COUNT(*) AS n,
98
+ ROUND(AVG((user_id IS NOT NULL)::INT), 4) AS uid_share,
99
+ ROUND(AVG((device_id IS NOT NULL)::INT), 4) AS device_share,
100
+ ROUND(COUNT(DISTINCT device_id)::DOUBLE / COUNT(DISTINCT user_id), 2) AS devices_per_user
101
+ FROM read_json_auto('data/verify-streaming-EVENTS*.json', sample_size=-1, union_by_name=true);
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * streaming.verify.mjs — thin wrapper around the story runner.
4
+ *
5
+ * All verification logic lives in the `stories` export of ./streaming.js;
6
+ * this script just streams the shards and delegates. It is equivalent to:
7
+ *
8
+ * node scripts/verify-stories.mjs dungeons/vertical/streaming/streaming.js --data-prefix verify-streaming
9
+ *
10
+ * Generate first:
11
+ * node scripts/verify-runner.mjs dungeons/vertical/streaming/streaming.js verify-streaming
12
+ * Run:
13
+ * node dungeons/vertical/streaming/streaming.verify.mjs [prefix] # default verify-streaming
14
+ */
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import readline from 'node:readline';
18
+ import { execFile } from 'node:child_process';
19
+ import { promisify } from 'node:util';
20
+ import { buildIdentityMap, evaluateStories, VERDICT_RANK } from '@ak--47/dungeon-master/verify';
21
+ import config, { stories } from './streaming.js';
22
+
23
+ const PREFIX = process.argv[2] || 'verify-streaming';
24
+ const prefixPath = path.join('data', PREFIX);
25
+
26
+ async function loadShards(suffix) {
27
+ // streaming load: full-fidelity EVENTS shards exceed the readFileSync cap
28
+ const dir = path.dirname(prefixPath), base = path.basename(prefixPath);
29
+ const out = [];
30
+ if (!fs.existsSync(dir)) return out;
31
+ for (const f of fs.readdirSync(dir).filter(f => f.startsWith(`${base}-${suffix}`) && f.endsWith('.json')).sort()) {
32
+ const rl = readline.createInterface({ input: fs.createReadStream(path.join(dir, f)), crlfDelay: Infinity });
33
+ for await (const line of rl) {
34
+ if (line.trim()) out.push(JSON.parse(line));
35
+ }
36
+ }
37
+ return out;
38
+ }
39
+
40
+ const events = await loadShards('EVENTS');
41
+ const profiles = await loadShards('USERS');
42
+ if (!events.length) {
43
+ console.error(`no shards at ${prefixPath}-EVENTS*.json — run: node scripts/verify-runner.mjs dungeons/vertical/streaming/streaming.js ${PREFIX}`);
44
+ process.exit(1);
45
+ }
46
+ console.log(`streaming — events=${events.length} users=${profiles.length} (${prefixPath})`);
47
+
48
+ const execFileP = promisify(execFile);
49
+ const runSql = async (sql) => {
50
+ const { stdout } = await execFileP('duckdb', ['-json', '-c', sql.replaceAll('{{PREFIX}}', prefixPath)], { maxBuffer: 64 * 1024 * 1024 });
51
+ return stdout.trim() ? JSON.parse(stdout) : [];
52
+ };
53
+
54
+ // Three stories are emulator-backed (lifecycle 7d/30d, uniques rolling-7 WAU)
55
+ // and read tiles by INDEX — generation shifts dates to the present but the
56
+ // 151-day span fixes every hook's tile position (see derivation notes in
57
+ // streaming.js). H3's gap audit and the identity invariants are DuckDB-backed:
58
+ // the ≥25d whole-stream gap only exists for the dropAll band (H1/H2 keep
59
+ // browse/search running through their windows), so the LAG scan isolates the
60
+ // campaign cohort without needing hashFloat in SQL.
61
+ const results = await evaluateStories(stories, events, {
62
+ profiles,
63
+ funnels: config.funnels,
64
+ identityMap: buildIdentityMap(profiles),
65
+ runSql,
66
+ });
67
+
68
+ let worst = 'NAILED';
69
+ for (const r of results) {
70
+ console.log(`${r.verdict.padEnd(7)} ${r.id}`);
71
+ for (const a of r.assertions) console.log(` ${a.verdict.padEnd(7)} ${a.detail}`);
72
+ if (VERDICT_RANK[r.verdict] < VERDICT_RANK[worst]) worst = r.verdict;
73
+ }
74
+ console.log(`\nworst verdict: ${worst}`);
75
+ process.exit(VERDICT_RANK[worst] >= VERDICT_RANK.STRONG ? 0 : 1);