@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.
- 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 +96 -0
- package/.claude/skills/create-project/provision.mjs +336 -0
- 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 +211 -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 +3 -2
- 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
|
@@ -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);
|