@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.
- 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/powertools/SKILL.md +75 -0
- package/.claude/skills/powertools/pt.mjs +91 -0
- package/.claude/skills/powertools/snapshot-project.mjs +124 -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 +224 -0
- package/HOOKS.md +566 -112
- package/README.md +30 -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 +7 -3
- 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 +151 -59
- 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 +298 -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,161 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- devtools.sql — human-eyeball inspection queries (v1.6)
|
|
3
|
+
-- ============================================================
|
|
4
|
+
-- Mirrors the story reads in devtools.js `stories`. Run after:
|
|
5
|
+
-- node scripts/verify-runner.mjs dungeons/vertical/devtools/devtools.js verify-devtools
|
|
6
|
+
--
|
|
7
|
+
-- Derivation notes:
|
|
8
|
+
-- - H1 uses MEDIAN: the extreme-value anomaly (10x build_duration_sec
|
|
9
|
+
-- at 0.3%) fattens means; medians isolate the clean 2x mechanism.
|
|
10
|
+
-- Bot events carry null build_status and fall out of status groups.
|
|
11
|
+
-- - H2/H9 exclude days 43-49: H6 recovery clones carry status
|
|
12
|
+
-- success/rolled_back only (never failed) and inflate deploy counts,
|
|
13
|
+
-- polluting both the failure-share and deploys-per-build reads.
|
|
14
|
+
-- - H3's cohort is the user_id hash (first GUID char in {2,3,4,d,e,f}),
|
|
15
|
+
-- NOT ai_assist — the copilot_integration feature (launchDay 30)
|
|
16
|
+
-- also flips ai_assist for feature adopters.
|
|
17
|
+
-- - H5/H9 normalize per-event / per-build: shares cancel persona event
|
|
18
|
+
-- multipliers, and the over/sweet pair cancels organic deploys-per-
|
|
19
|
+
-- build entirely (0.6/1.5 = 0.40 exact).
|
|
20
|
+
-- - H10 is NOT visible here: funnel-post scales gaps within funnel
|
|
21
|
+
-- instances; cross-event MIN→MIN SQL flattens it. See the emulator
|
|
22
|
+
-- timeToConvert assertion in devtools.js stories.
|
|
23
|
+
|
|
24
|
+
-- Hook 1: BUILD FAILURE CASCADE — failed builds 2x duration (median)
|
|
25
|
+
SELECT build_status, COUNT(*) AS n,
|
|
26
|
+
ROUND(MEDIAN(TRY_CAST(build_duration_sec AS DOUBLE)), 0) AS med_dur,
|
|
27
|
+
ROUND(AVG(TRY_CAST(build_duration_sec AS DOUBLE)), 0) AS avg_dur
|
|
28
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
29
|
+
WHERE event = 'build completed'
|
|
30
|
+
GROUP BY build_status ORDER BY med_dur DESC;
|
|
31
|
+
|
|
32
|
+
-- Hook 2: NIGHT DEPLOY RISK — ~52% night vs ~21% day failure share
|
|
33
|
+
-- (recovery window days 43-49 excluded; H6 clones dilute it)
|
|
34
|
+
WITH ev AS (
|
|
35
|
+
SELECT deploy_status, hour(time::TIMESTAMP) AS hr,
|
|
36
|
+
date_diff('day', TIMESTAMP '2026-01-01 00:00:00', time::TIMESTAMP) AS day_idx
|
|
37
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
38
|
+
WHERE event = 'deployment completed'
|
|
39
|
+
)
|
|
40
|
+
SELECT CASE WHEN hr >= 22 OR hr < 6 THEN 'night' ELSE 'day' END AS bucket,
|
|
41
|
+
COUNT(*) AS deploys,
|
|
42
|
+
ROUND(AVG((deploy_status = 'failed')::INT), 4) AS failure_share
|
|
43
|
+
FROM ev WHERE day_idx NOT BETWEEN 43 AND 49
|
|
44
|
+
GROUP BY (CASE WHEN hr >= 22 OR hr < 6 THEN 'night' ELSE 'day' END);
|
|
45
|
+
|
|
46
|
+
-- Hook 3: COPILOT PR VELOCITY — hash cohort ~1.5x PRs/user
|
|
47
|
+
WITH pu AS (
|
|
48
|
+
SELECT user_id::VARCHAR AS uid,
|
|
49
|
+
(ascii(substr(user_id::VARCHAR, 1, 1)) % 10 < 3) AS copilot,
|
|
50
|
+
COUNT(*) FILTER (WHERE event = 'pull request created') AS prs
|
|
51
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
52
|
+
GROUP BY 1, 2
|
|
53
|
+
)
|
|
54
|
+
SELECT copilot, COUNT(*) AS users,
|
|
55
|
+
ROUND(SUM(prs)::DOUBLE / COUNT(*), 2) AS prs_per_user
|
|
56
|
+
FROM pu GROUP BY copilot ORDER BY copilot;
|
|
57
|
+
|
|
58
|
+
-- Hook 4: ON-CALL FATIGUE — >20 alerts → ~2.6x mean response time
|
|
59
|
+
WITH pu AS (
|
|
60
|
+
SELECT user_id::VARCHAR AS uid,
|
|
61
|
+
COUNT(*) FILTER (WHERE event = 'alert triggered') AS alerts,
|
|
62
|
+
AVG(TRY_CAST(response_time_minutes AS DOUBLE)) FILTER (WHERE event IN ('incident created', 'incident resolved')) AS rt,
|
|
63
|
+
COUNT(*) FILTER (WHERE event IN ('incident created', 'incident resolved')) AS incidents
|
|
64
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
65
|
+
GROUP BY 1
|
|
66
|
+
)
|
|
67
|
+
SELECT (alerts > 20) AS fatigued, COUNT(*) AS users,
|
|
68
|
+
ROUND(AVG(rt), 1) AS mean_response_min,
|
|
69
|
+
ROUND(AVG(1 + LEAST(alerts / 20.0, 3)) FILTER (WHERE alerts > 20), 2) AS mean_engineered_mult
|
|
70
|
+
FROM pu WHERE incidents > 0 GROUP BY (alerts > 20) ORDER BY 1;
|
|
71
|
+
|
|
72
|
+
-- Hook 5: OSS POWER USAGE — active oss build share ~1.3x active non-oss
|
|
73
|
+
WITH us AS (
|
|
74
|
+
SELECT distinct_id::VARCHAR AS uid, segment
|
|
75
|
+
FROM read_json_auto('data/verify-devtools-USERS*.json', sample_size=-1, union_by_name=true)
|
|
76
|
+
), pu AS (
|
|
77
|
+
SELECT user_id::VARCHAR AS uid, COUNT(*) AS n_ev,
|
|
78
|
+
COUNT(*) FILTER (WHERE event = 'build completed') AS builds,
|
|
79
|
+
COUNT(*) FILTER (WHERE event = 'deployment completed') AS deploys
|
|
80
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
81
|
+
GROUP BY 1
|
|
82
|
+
)
|
|
83
|
+
SELECT (u.segment = 'oss_user') AS is_oss, COUNT(*) AS users,
|
|
84
|
+
ROUND(SUM(builds)::DOUBLE / SUM(n_ev), 4) AS build_share,
|
|
85
|
+
ROUND(SUM(deploys)::DOUBLE / SUM(n_ev), 4) AS deploy_share
|
|
86
|
+
FROM pu p JOIN us u USING (uid)
|
|
87
|
+
WHERE n_ev > 25
|
|
88
|
+
GROUP BY (u.segment = 'oss_user') ORDER BY is_oss;
|
|
89
|
+
|
|
90
|
+
-- Hook 6: POST-OUTAGE RECOVERY — deploys ~3.7x on days 44-47
|
|
91
|
+
-- (ratio-of-ratios vs builds cancels the growth ramp)
|
|
92
|
+
WITH ev AS (
|
|
93
|
+
SELECT event, date_diff('day', TIMESTAMP '2026-01-01 00:00:00', time::TIMESTAMP) AS day_idx
|
|
94
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
95
|
+
WHERE event IN ('deployment completed', 'build completed')
|
|
96
|
+
)
|
|
97
|
+
SELECT CASE WHEN day_idx BETWEEN 44 AND 47 THEN 'recovery' ELSE 'baseline' END AS zone,
|
|
98
|
+
COUNT(*) FILTER (WHERE event = 'deployment completed') AS deploys,
|
|
99
|
+
COUNT(*) FILTER (WHERE event = 'build completed') AS builds,
|
|
100
|
+
ROUND(COUNT(*) FILTER (WHERE event = 'deployment completed')::DOUBLE
|
|
101
|
+
/ COUNT(*) FILTER (WHERE event = 'build completed'), 3) AS deploys_per_build
|
|
102
|
+
FROM ev
|
|
103
|
+
WHERE day_idx BETWEEN 35 AND 41 OR day_idx BETWEEN 44 AND 47 OR day_idx BETWEEN 49 AND 55
|
|
104
|
+
GROUP BY (CASE WHEN day_idx BETWEEN 44 AND 47 THEN 'recovery' ELSE 'baseline' END);
|
|
105
|
+
|
|
106
|
+
-- Hook 7: DEVOPS PROFILE ENRICHMENT — repos_connected is the crisp
|
|
107
|
+
-- signal (default [0]); team_size contrast is devops vs junior
|
|
108
|
+
-- (full_stack/oss default pool mean is ~24, NOT ~10)
|
|
109
|
+
SELECT segment, COUNT(*) AS users,
|
|
110
|
+
ROUND(AVG(TRY_CAST(team_size AS DOUBLE)), 1) AS avg_team,
|
|
111
|
+
ROUND(AVG(TRY_CAST(repos_connected AS DOUBLE)), 1) AS avg_repos,
|
|
112
|
+
MODE(experience_level) AS mode_exp
|
|
113
|
+
FROM read_json_auto('data/verify-devtools-USERS*.json', sample_size=-1, union_by_name=true)
|
|
114
|
+
GROUP BY segment ORDER BY avg_repos DESC;
|
|
115
|
+
|
|
116
|
+
-- Hook 8: ENTERPRISE FUNNEL LIFT — free/team mv-per-deploy ~0.63x paid
|
|
117
|
+
-- (per-deploy normalization cancels H6/H9 deploy inflation)
|
|
118
|
+
WITH pu AS (
|
|
119
|
+
SELECT user_id::VARCHAR AS uid, ANY_VALUE(subscription_tier) AS tier,
|
|
120
|
+
COUNT(*) FILTER (WHERE event = 'monitoring dashboard viewed') AS mv,
|
|
121
|
+
COUNT(*) FILTER (WHERE event = 'deployment completed') AS dep
|
|
122
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
123
|
+
WHERE event IN ('monitoring dashboard viewed', 'deployment completed')
|
|
124
|
+
GROUP BY 1
|
|
125
|
+
)
|
|
126
|
+
SELECT CASE WHEN tier IN ('enterprise', 'business') THEN 'paid' ELSE 'free_team' END AS grp,
|
|
127
|
+
COUNT(*) AS users,
|
|
128
|
+
ROUND(SUM(mv)::DOUBLE / NULLIF(SUM(dep), 0), 4) AS mv_per_deploy
|
|
129
|
+
FROM pu WHERE tier IS NOT NULL
|
|
130
|
+
GROUP BY (CASE WHEN tier IN ('enterprise', 'business') THEN 'paid' ELSE 'free_team' END);
|
|
131
|
+
|
|
132
|
+
-- Hook 9: BUILD-COUNT MAGIC NUMBER — deploys-per-build by bucket
|
|
133
|
+
-- (full_stack only; recovery-window deploys excluded; over/sweet ≈ 0.40)
|
|
134
|
+
WITH us AS (
|
|
135
|
+
SELECT distinct_id::VARCHAR AS uid, segment
|
|
136
|
+
FROM read_json_auto('data/verify-devtools-USERS*.json', sample_size=-1, union_by_name=true)
|
|
137
|
+
), pu AS (
|
|
138
|
+
SELECT user_id::VARCHAR AS uid,
|
|
139
|
+
COUNT(*) FILTER (WHERE event = 'build completed') AS builds,
|
|
140
|
+
COUNT(*) FILTER (WHERE event = 'deployment completed'
|
|
141
|
+
AND date_diff('day', TIMESTAMP '2026-01-01 00:00:00', time::TIMESTAMP) NOT BETWEEN 43 AND 49) AS deploys
|
|
142
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
143
|
+
GROUP BY 1
|
|
144
|
+
)
|
|
145
|
+
SELECT CASE WHEN builds BETWEEN 15 AND 30 THEN 'sweet'
|
|
146
|
+
WHEN builds >= 31 THEN 'over' ELSE 'base' END AS bucket,
|
|
147
|
+
COUNT(*) AS users,
|
|
148
|
+
ROUND(SUM(deploys)::DOUBLE / SUM(builds), 4) AS deploys_per_build
|
|
149
|
+
FROM pu p JOIN us u USING (uid)
|
|
150
|
+
WHERE u.segment = 'full_stack' AND builds >= 1
|
|
151
|
+
GROUP BY (CASE WHEN builds BETWEEN 15 AND 30 THEN 'sweet' WHEN builds >= 31 THEN 'over' ELSE 'base' END)
|
|
152
|
+
ORDER BY bucket;
|
|
153
|
+
|
|
154
|
+
-- Hook 10: BUILD-DEPLOY TTC BY TIER — not visible in cross-event SQL.
|
|
155
|
+
-- funnel-post rewrites step timestamps within funnel instances; the
|
|
156
|
+
-- emulator's timeToConvert (see devtools.js stories) is the honest
|
|
157
|
+
-- read. This query only confirms the tier populations exist.
|
|
158
|
+
SELECT subscription_tier, COUNT(DISTINCT user_id) AS users
|
|
159
|
+
FROM read_json_auto('data/verify-devtools-EVENTS*.json', sample_size=-1, union_by_name=true)
|
|
160
|
+
WHERE event IN ('build completed', 'deployment completed') AND subscription_tier IS NOT NULL
|
|
161
|
+
GROUP BY subscription_tier ORDER BY users DESC;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* devtools.verify.mjs — thin wrapper around the story runner.
|
|
4
|
+
*
|
|
5
|
+
* All verification logic lives in the `stories` export of
|
|
6
|
+
* ./devtools.js; this script just streams the shards and
|
|
7
|
+
* delegates. It is equivalent to:
|
|
8
|
+
*
|
|
9
|
+
* node scripts/verify-stories.mjs dungeons/vertical/devtools/devtools.js --data-prefix verify-devtools
|
|
10
|
+
*
|
|
11
|
+
* Generate first:
|
|
12
|
+
* node scripts/verify-runner.mjs dungeons/vertical/devtools/devtools.js verify-devtools
|
|
13
|
+
* Run:
|
|
14
|
+
* node dungeons/vertical/devtools/devtools.verify.mjs [prefix] # default verify-devtools
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import readline from 'node:readline';
|
|
19
|
+
import { execFile } from 'node:child_process';
|
|
20
|
+
import { promisify } from 'node:util';
|
|
21
|
+
import { buildIdentityMap, evaluateStories, VERDICT_RANK } from '@ak--47/dungeon-master/verify';
|
|
22
|
+
import config, { stories } from './devtools.js';
|
|
23
|
+
|
|
24
|
+
const PREFIX = process.argv[2] || 'verify-devtools';
|
|
25
|
+
const prefixPath = path.join('data', PREFIX);
|
|
26
|
+
|
|
27
|
+
async function loadShards(suffix) {
|
|
28
|
+
// streaming load: full-fidelity EVENTS shards exceed the readFileSync cap
|
|
29
|
+
const dir = path.dirname(prefixPath), base = path.basename(prefixPath);
|
|
30
|
+
const out = [];
|
|
31
|
+
if (!fs.existsSync(dir)) return out;
|
|
32
|
+
for (const f of fs.readdirSync(dir).filter(f => f.startsWith(`${base}-${suffix}`) && f.endsWith('.json')).sort()) {
|
|
33
|
+
const rl = readline.createInterface({ input: fs.createReadStream(path.join(dir, f)), crlfDelay: Infinity });
|
|
34
|
+
for await (const line of rl) {
|
|
35
|
+
if (line.trim()) out.push(JSON.parse(line));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const events = await loadShards('EVENTS');
|
|
42
|
+
const profiles = await loadShards('USERS');
|
|
43
|
+
if (!events.length) {
|
|
44
|
+
console.error(`no shards at ${prefixPath}-EVENTS*.json — run: node scripts/verify-runner.mjs dungeons/vertical/devtools/devtools.js ${PREFIX}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
console.log(`devtools — events=${events.length} users=${profiles.length} (${prefixPath})`);
|
|
48
|
+
|
|
49
|
+
const execFileP = promisify(execFile);
|
|
50
|
+
const runSql = async (sql) => {
|
|
51
|
+
const { stdout } = await execFileP('duckdb', ['-json', '-c', sql.replaceAll('{{PREFIX}}', prefixPath)], { maxBuffer: 64 * 1024 * 1024 });
|
|
52
|
+
return stdout.trim() ? JSON.parse(stdout) : [];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Nine stories are DuckDB-backed (behavioral cohorts, ratio-of-ratios reads
|
|
56
|
+
// that cancel soup/persona confounds); H10 dispatches to emulateBreakdown's
|
|
57
|
+
// timeToConvert — funnel-post gap scaling is only visible to funnel-instance
|
|
58
|
+
// semantics, not cross-event SQL (greedy MIN→MIN picks flatten it).
|
|
59
|
+
const results = await evaluateStories(stories, events, {
|
|
60
|
+
profiles,
|
|
61
|
+
funnels: config.funnels,
|
|
62
|
+
identityMap: buildIdentityMap(profiles),
|
|
63
|
+
runSql,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
let worst = 'NAILED';
|
|
67
|
+
for (const r of results) {
|
|
68
|
+
console.log(`${r.verdict.padEnd(7)} ${r.id}`);
|
|
69
|
+
for (const a of r.assertions) console.log(` ${a.verdict.padEnd(7)} ${a.detail}`);
|
|
70
|
+
if (VERDICT_RANK[r.verdict] < VERDICT_RANK[worst]) worst = r.verdict;
|
|
71
|
+
}
|
|
72
|
+
console.log(`\nworst verdict: ${worst}`);
|
|
73
|
+
process.exit(VERDICT_RANK[worst] >= VERDICT_RANK.STRONG ? 0 : 1);
|