@ak--47/dungeon-master 1.7.0 → 1.8.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 -11
- package/.claude/skills/create-dungeon/SKILL.md +49 -10
- package/.claude/skills/create-project/SKILL.md +22 -3
- package/.claude/skills/create-project/context.mjs +89 -0
- package/.claude/skills/create-project/provision.mjs +1 -60
- package/.claude/skills/headless-build/SKILL.md +18 -1
- package/.claude/skills/powertools/SKILL.md +20 -1
- package/.claude/skills/release-check/SKILL.md +99 -0
- package/.claude/skills/verify-dungeon/SKILL.md +71 -16
- package/.claude/skills/verify-dungeon/references/counting-semantics.md +14 -0
- package/.claude/skills/verify-dungeon/references/report-format.md +18 -1
- package/.claude/skills/verify-dungeon/references/sql-recipes.md +36 -1
- package/.claude/skills/warehouse-metrics/GAPS-template.md +34 -0
- package/.claude/skills/warehouse-metrics/SKILL.md +105 -0
- package/.claude/skills/warehouse-metrics/deploy.mjs +651 -0
- package/.claude/skills/write-hooks/SKILL.md +33 -3
- package/CHANGELOG.md +142 -0
- package/HOOKS.md +105 -5
- package/README.md +228 -0
- package/docs/guides/1.8.0-upgrade-guide.md +151 -0
- package/dungeons/technical/warehouse.js +187 -0
- package/index.js +116 -2
- package/lib/core/config-validator.js +21 -0
- package/lib/core/dungeon-loader.js +1 -1
- package/lib/core/storage.js +51 -3
- package/lib/generators/standalone.js +248 -0
- package/lib/generators/warehouse.js +828 -0
- package/lib/orchestrators/mixpanel-sender.js +27 -2
- package/lib/orchestrators/user-loop.js +1 -0
- package/lib/templates/story-spec.schema.json +41 -16
- package/lib/utils/utils.js +37 -12
- package/lib/verify/index.js +1 -0
- package/lib/verify/story-runner.js +71 -8
- package/lib/verify/warehouse.js +683 -0
- package/package.json +4 -2
- package/scripts/verify-stories.mjs +150 -44
- package/types.d.ts +303 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: analyze-soup
|
|
3
3
|
description: Use when investigating TimeSoup parameters, diagnosing event-distribution shape, or comparing soup configs — runs a dungeon locally and analyzes time distribution at week/day/hour/minute granularities, producing a soup-analysis.md diagnostic report.
|
|
4
|
-
argument-hint: [dungeon path, e.g. dungeons/technical/simplest.js]
|
|
4
|
+
argument-hint: '[dungeon path, e.g. dungeons/technical/simplest.js]'
|
|
5
5
|
model: claude-opus-4-6
|
|
6
6
|
effort: max
|
|
7
7
|
---
|
|
@@ -10,6 +10,12 @@ effort: max
|
|
|
10
10
|
|
|
11
11
|
Run a dungeon and analyze the time distribution of generated events to evaluate TimeSoup parameters.
|
|
12
12
|
|
|
13
|
+
Analyze user `eventData` / EVENTS shards only. `standaloneEvents` runs on a
|
|
14
|
+
fixed cadence before the user loop; `warehouseMetrics` materializes tables
|
|
15
|
+
afterward. Neither measures TimeSoup. Report their cadence or table checks
|
|
16
|
+
separately through `/verify-dungeon`, and preserve warehouse files for
|
|
17
|
+
`/warehouse-metrics`. Never count standalone synthetic ids as people.
|
|
18
|
+
|
|
13
19
|
**Dungeon file:** `$ARGUMENTS` (default: `dungeons/technical/simplest.js`)
|
|
14
20
|
|
|
15
21
|
## Step 1: Run the Dungeon
|
|
@@ -17,15 +23,20 @@ Run a dungeon and analyze the time distribution of generated events to evaluate
|
|
|
17
23
|
Run the dungeon with forced local-only settings:
|
|
18
24
|
|
|
19
25
|
```bash
|
|
20
|
-
|
|
21
|
-
node -e "
|
|
26
|
+
node --input-type=module -e "
|
|
22
27
|
import generate from './index.js';
|
|
23
28
|
import config from './$ARGUMENTS';
|
|
24
|
-
const result = await generate({ ...config, writeToDisk: true, format: 'json', token: '', verbose: true, name: 'soup-analysis' });
|
|
29
|
+
const result = await generate({ ...config, writeToDisk: true, gzip: false, format: 'json', token: '', verbose: true, name: 'soup-analysis' });
|
|
25
30
|
console.log('Events:', result.eventCount, 'Users:', result.userCount);
|
|
26
31
|
"
|
|
27
32
|
```
|
|
28
33
|
|
|
34
|
+
Choose an unused run name before executing; `soup-analysis` below is an example.
|
|
35
|
+
Record the explicit data prefix (`data/soup-analysis`) and use it in every query.
|
|
36
|
+
If that prefix already exists, choose another and update the query paths. Do not
|
|
37
|
+
prune or overwrite an earlier run. Use `<prefix>-EVENTS*.json` with
|
|
38
|
+
`union_by_name=true` for batched output; never glob STANDALONE or WAREHOUSE into it.
|
|
39
|
+
|
|
29
40
|
Wait for generation to complete. Note the event count and EPS.
|
|
30
41
|
|
|
31
42
|
## Step 2: Query with DuckDB
|
|
@@ -147,7 +158,7 @@ Create `soup-analysis.md` with the structure below. For a user dungeon
|
|
|
147
158
|
(`dungeons/user/<name>/<name>.js`) write it into the dungeon's folder
|
|
148
159
|
(`dungeons/user/<name>/soup-analysis.md`) — everything about a dungeon lives in
|
|
149
160
|
its folder. Otherwise write it to the project root. (The generated
|
|
150
|
-
`./data/soup-analysis-EVENTS.json` is
|
|
161
|
+
`./data/soup-analysis-EVENTS.json` is retained verification data; leave it in
|
|
151
162
|
`./data/`.) Contents:
|
|
152
163
|
|
|
153
164
|
1. **Config**: The soup parameters used (peaks, deviation, mean, numDays)
|
|
@@ -163,10 +174,9 @@ its folder. Otherwise write it to the project root. (The generated
|
|
|
163
174
|
- **Last day spike**: < 1.5x average = PASS, < 2x = WARN, > 2x = FAIL
|
|
164
175
|
- **Hourly pattern**: Should show visible peaks but no single hour > 5x average
|
|
165
176
|
|
|
166
|
-
## Step 4:
|
|
167
|
-
|
|
168
|
-
```bash
|
|
169
|
-
npm run prune
|
|
170
|
-
```
|
|
177
|
+
## Step 4: Preserve artifacts
|
|
171
178
|
|
|
172
|
-
|
|
179
|
+
Record the prefix and files in the report. Keep warehouse tables and the matching
|
|
180
|
+
manifest until deployment completes. Cleanup requires user consent and an explicit
|
|
181
|
+
list of this run's files. Never run blanket prune. Keep `soup-analysis.md` for
|
|
182
|
+
comparison across runs unless the user asks to remove it.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-dungeon
|
|
3
|
-
description: Use when authoring a new dungeon-master config from an app description
|
|
4
|
-
argument-hint: [free-text app description, e.g. "AI meeting assistant" or "B2B logistics platform"]
|
|
3
|
+
description: Use when authoring a new dungeon-master config from an app description, including standaloneEvents cadence streams and warehouseMetrics source tables. Designs events, funnels, properties, identity model, and macro/soup. SCHEMA ONLY; engineered story trends and hooks are added separately by write-hooks.
|
|
4
|
+
argument-hint: '[free-text app description, e.g. "AI meeting assistant" or "B2B logistics platform"]'
|
|
5
5
|
model: claude-opus-4-6
|
|
6
6
|
effort: max
|
|
7
7
|
---
|
|
@@ -87,9 +87,9 @@ import dayjs from "dayjs";
|
|
|
87
87
|
import utc from "dayjs/plugin/utc.js";
|
|
88
88
|
dayjs.extend(utc);
|
|
89
89
|
import "dotenv/config";
|
|
90
|
-
import * as u from "
|
|
90
|
+
import * as u from "../../../lib/utils/utils.js";
|
|
91
91
|
import * as v from "ak-tools";
|
|
92
|
-
/** @typedef {import("
|
|
92
|
+
/** @typedef {import("../../../types").Dungeon} Config */
|
|
93
93
|
|
|
94
94
|
// ── OVERVIEW ──
|
|
95
95
|
/*
|
|
@@ -133,8 +133,8 @@ const config = {
|
|
|
133
133
|
numUsers: NUM_USERS,
|
|
134
134
|
avgEventsPerUserPerDay: EVENTS_PER_DAY,
|
|
135
135
|
format: "json",
|
|
136
|
-
gzip:
|
|
137
|
-
writeToDisk:
|
|
136
|
+
gzip: false,
|
|
137
|
+
writeToDisk: true,
|
|
138
138
|
concurrency: 1,
|
|
139
139
|
macro: "flat", // optional — see "Trend shape" below
|
|
140
140
|
soup: "growth", // optional
|
|
@@ -319,7 +319,40 @@ group attributes. Skip for B2C apps.
|
|
|
319
319
|
Slowly-changing dimensions for plan tier, role, etc. JSDoc on `SCDProp` covers
|
|
320
320
|
type/frequency/timing/values/max.
|
|
321
321
|
|
|
322
|
-
### 7.
|
|
322
|
+
### 7. Metric data surfaces (v1.8.0)
|
|
323
|
+
|
|
324
|
+
Author these schemas here when the app needs system telemetry or warehouse tables.
|
|
325
|
+
Requests that call these surfaces "v2" still target v1.8.0; do not bump the version.
|
|
326
|
+
|
|
327
|
+
| Surface | Required design | Time settings | Output |
|
|
328
|
+
|---|---|---|---|
|
|
329
|
+
| `standaloneEvents` | `event`, declared `dimensions` and `properties`; optional `distinctIdFrom` naming a dimension | `cadence: 'hour'`, `'day'`, or `'week'` | `result.standaloneEventData`, `-STANDALONE*.json`; imports as events |
|
|
330
|
+
| `warehouseMetrics` | `name`, `type: 'additive'` or `'point-in-time'`, `source`; declare extra `columns` | `grain: 'day'`, `'week'`, or `'month'`, optional `history` bucket count | `result.warehouseMetricData`, `-WAREHOUSE-*` tables and `-WAREHOUSE-MANIFEST.json`; separate deploy |
|
|
331
|
+
|
|
332
|
+
`standaloneEvents` generates before the user loop. Each cadence tick emits one
|
|
333
|
+
record per dimension cross-product row. Its synthetic `distinct_id` identifies a
|
|
334
|
+
series, never a person. Do not put these events in funnels, retention, identity
|
|
335
|
+
stitch checks, or people counts. They do not inherit user metadata or superProps.
|
|
336
|
+
|
|
337
|
+
Standalone property functions receive `{ time, config, dimensions, tickIndex,
|
|
338
|
+
tickCount, cadence, event }`, with `time` in milliseconds. Guard `tickCount <= 1`
|
|
339
|
+
before dividing by `tickCount - 1`. Declare every property and dimension here.
|
|
340
|
+
|
|
341
|
+
Warehouse sources reference only user `events[]`, including names in both
|
|
342
|
+
`source.event` (plus) and optional `source.minus`. A `standaloneEvents` stream
|
|
343
|
+
cannot be a warehouse source. Declare `source.property` and every `source.groupBy`
|
|
344
|
+
key on every plus and minus event, or in `superProps`. `sum` and `avg` require
|
|
345
|
+
`source.property`; point-in-time disallows `avg` and `dau`. Use at most two group
|
|
346
|
+
keys. `sparse: true` applies only to point-in-time metrics.
|
|
347
|
+
|
|
348
|
+
Warehouse materialization runs after the user loop. Extra column functions receive
|
|
349
|
+
`{ value, row, time, bucketIndex, bucketCount, grain, isBackfill, seriesKey, spec,
|
|
350
|
+
config }`. `time` is milliseconds. `history` adds synthetic pre-window buckets;
|
|
351
|
+
it does not create historical user events. Keep `timeColumn`, `valueColumn`, group
|
|
352
|
+
keys, and extra `columns` distinct and declared. Read `dungeons/technical/warehouse.js`
|
|
353
|
+
and the warehouse interfaces in `types.d.ts` before choosing measures or backfill.
|
|
354
|
+
|
|
355
|
+
### 8. Hook function — DO NOT WRITE
|
|
323
356
|
|
|
324
357
|
Skip the `hook:` field entirely (engine defaults to pass-through). The
|
|
325
358
|
`write-hooks` skill picks this up and engineers the trends.
|
|
@@ -620,10 +653,16 @@ This keeps `dungeons/user/` organized — EVERYTHING about this dungeon lives in
|
|
|
620
653
|
the same folder: `hook-results.md` + `hook-query-log.txt` +
|
|
621
654
|
`<name>-verifications.sql` (from `verify-dungeon`), `soup-analysis.md` (from
|
|
622
655
|
`analyze-soup`), briefs, schema CSV/JSON, example data. The only thing kept
|
|
623
|
-
outside is the
|
|
624
|
-
|
|
656
|
+
outside is the verification data the runs write to `./data/`. Preserve the exact
|
|
657
|
+
run prefix and its files until verification and warehouse deployment are complete.
|
|
625
658
|
|
|
626
659
|
Do NOT inject hooks. Do NOT use `subscription`, `attribution`, `geo`,
|
|
627
660
|
`features`, or `anomalies` (the engine will silently strip them and warn).
|
|
628
661
|
|
|
629
|
-
When done,
|
|
662
|
+
When done, hand off to `/write-hooks` when trends are needed, then `/verify-dungeon`.
|
|
663
|
+
For artifact generation use local `writeToDisk: true`, `format: 'json'`,
|
|
664
|
+
`gzip: false`, and an explicit unique run name. Verification runs must disable
|
|
665
|
+
sending with a top-level `token: ''` override. Provision with `/create-project`
|
|
666
|
+
after verification. If `warehouseMetrics` exists, route to `/warehouse-metrics`
|
|
667
|
+
with the verified disk artifact prefix after project provisioning. Keep those
|
|
668
|
+
files; ordinary event import does not deploy warehouse tables.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-project
|
|
3
3
|
description: Use when an existing dungeon needs a real Mixpanel project provisioned before sending data — creates the project, sets timezone UTC, mints a scoped service account, adds the dungeon's group keys, uploads business context (AI context), and writes the resulting credentials back into the dungeon so it "just runs". Follows create-dungeon / write-hooks / verify-dungeon.
|
|
4
|
-
argument-hint: [dungeon path, e.g. dungeons/user/shopstream/shopstream.js]
|
|
4
|
+
argument-hint: '[dungeon path, e.g. dungeons/user/shopstream/shopstream.js]'
|
|
5
5
|
model: claude-opus-4-6
|
|
6
6
|
effort: max
|
|
7
7
|
---
|
|
@@ -25,7 +25,7 @@ in order:
|
|
|
25
25
|
1. **createProject** — name derived from the dungeon's `OVERVIEW` (`NAME:` line), region `US`, timezone `UTC` (set as a follow-up by the endpoint).
|
|
26
26
|
2. **mintServiceAccount** — `admin`, expires `+30 days`, scoped to the new project. This is what the dungeon uses to **send** data.
|
|
27
27
|
3. **addGroupKey** — one per `groupKeys` entry in the dungeon (`property_name` + a titleized `display_name`). Skipped if the dungeon has no group keys.
|
|
28
|
-
4. **setBusinessContext** — markdown built from the dungeon's `OVERVIEW` comment block plus the `stories` named export (per story: `narrative` + `mixpanelReport` + intentional deviations); dungeons without stories fall back to the `HOOK STORIES` comment scrape (via the package's `extractComments`). Plus an events/funnels/props/group-keys summary, capped at 50k chars. The dry-run plan prints which source was used.
|
|
28
|
+
4. **setBusinessContext** — markdown built from the dungeon's `OVERVIEW` comment block plus the `stories` named export (per story: `narrative` + `mixpanelReport` + intentional deviations); dungeons without stories fall back to the `HOOK STORIES` comment scrape (via the package's `extractComments`). Plus an events/funnels/props/group-keys summary and separate standalone-event and warehouse-metric summaries, capped at 50k chars. The dry-run plan prints which source was used.
|
|
29
29
|
5. **write-back** — replaces the dungeon's `credentials: { … }` block with `{ token, projectId, serviceAccount, serviceSecret, region }`.
|
|
30
30
|
|
|
31
31
|
**Always creates a fresh project.** Re-running mints a new project and overwrites
|
|
@@ -83,7 +83,26 @@ run command:
|
|
|
83
83
|
node scripts/run-dungeon.mjs <dungeon-path>
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
##
|
|
86
|
+
## Metric handoff (v1.8.0)
|
|
87
|
+
|
|
88
|
+
Verify `standaloneEvents` and `warehouseMetrics` locally before provisioning.
|
|
89
|
+
Keep the verified run's disk files and record its explicit prefix. Cadence events
|
|
90
|
+
import through the normal send path; their synthetic `distinct_id` values must
|
|
91
|
+
never become people counts or identity-model evidence.
|
|
92
|
+
|
|
93
|
+
After credentials are written, route `warehouseMetrics` to `/warehouse-metrics`
|
|
94
|
+
with that prefix and `credentials.projectId`. Require local uncompressed table
|
|
95
|
+
files (`writeToDisk: true`, `gzip: false`) and the matching warehouse manifest.
|
|
96
|
+
Review the deploy dry run before obtaining consent for live writes. Project
|
|
97
|
+
provisioning alone does not load BigQuery tables or save warehouse metrics.
|
|
98
|
+
|
|
99
|
+
The pure `context.mjs` helper summarizes cadence, dimension keys and cardinalities,
|
|
100
|
+
property names, and synthetic identity separately from person events. Warehouse
|
|
101
|
+
summaries include sources, measures, grain, type, point-in-time baseline, history,
|
|
102
|
+
dimension and output columns, aggregation, and the separate deployment handoff.
|
|
103
|
+
It does not evaluate property functions or include config credentials.
|
|
104
|
+
|
|
105
|
+
## Provisioning errors
|
|
87
106
|
|
|
88
107
|
- **Missing `BEARER_TOKEN` / `ORG_ID`** — orchestrator exits; have the user fix `.env`.
|
|
89
108
|
- **`createProject` fails** — nothing is provisioned; surface the power-tools error (`{ error }` or `{ errors:[{param,message}] }`) verbatim and stop.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export function buildContext(name, config, comments, groupKeys, stories) {
|
|
2
|
+
const parts = [`# ${name}`, ''];
|
|
3
|
+
if (comments.overview) parts.push(comments.overview, '');
|
|
4
|
+
|
|
5
|
+
if (stories?.length) {
|
|
6
|
+
parts.push('## Engineered Behaviors', '');
|
|
7
|
+
parts.push(`${stories.length} machine-verified story patterns (from the dungeon's \`stories\` export):`, '');
|
|
8
|
+
for (const s of stories) {
|
|
9
|
+
const arch = s.archetype ? ` — ${s.archetype}` : '';
|
|
10
|
+
parts.push(`### ${s.id}${arch}`, '');
|
|
11
|
+
if (s.narrative) parts.push(String(s.narrative).trim(), '');
|
|
12
|
+
if (s.mixpanelReport && typeof s.mixpanelReport === 'object') {
|
|
13
|
+
parts.push('How to see it in Mixpanel:');
|
|
14
|
+
for (const [k, v] of Object.entries(s.mixpanelReport)) {
|
|
15
|
+
parts.push(`- **${k}**: ${typeof v === 'string' ? v : JSON.stringify(v)}`);
|
|
16
|
+
}
|
|
17
|
+
parts.push('');
|
|
18
|
+
}
|
|
19
|
+
if (Array.isArray(s.intentionalDeviations) && s.intentionalDeviations.length) {
|
|
20
|
+
parts.push('Notes:', ...s.intentionalDeviations.map((d) => `- ${d}`), '');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} else if (comments.hookStories) {
|
|
24
|
+
parts.push('## Engineered Behaviors', '', comments.hookStories, '');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
parts.push('## Schema', '');
|
|
28
|
+
const events = config.events || [];
|
|
29
|
+
parts.push(`### Events (${events.length})`);
|
|
30
|
+
for (const e of events) {
|
|
31
|
+
const props = e.properties ? Object.keys(e.properties).join(', ') : '';
|
|
32
|
+
const weight = e.weight != null ? ` (weight ${e.weight})` : '';
|
|
33
|
+
parts.push(`- ${e.event}${weight}${props ? ` — ${props}` : ''}`);
|
|
34
|
+
}
|
|
35
|
+
parts.push('');
|
|
36
|
+
|
|
37
|
+
const funnels = config.funnels || [];
|
|
38
|
+
if (funnels.length) {
|
|
39
|
+
parts.push(`### Funnels (${funnels.length})`);
|
|
40
|
+
for (const f of funnels) {
|
|
41
|
+
const seq = (f.sequence || []).join(' → ');
|
|
42
|
+
const rate = f.conversionRate != null ? ` (${f.conversionRate}%)` : '';
|
|
43
|
+
parts.push(`- ${f.name || '(unnamed)'}: ${seq}${rate}`);
|
|
44
|
+
}
|
|
45
|
+
parts.push('');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (groupKeys.length) {
|
|
49
|
+
parts.push('### Group keys', ...groupKeys.map((g) => `- ${g.property_name} (${g.display_name})`), '');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const standaloneEvents = config.standaloneEvents || [];
|
|
53
|
+
if (standaloneEvents.length) {
|
|
54
|
+
parts.push(`### Standalone events (${standaloneEvents.length})`);
|
|
55
|
+
parts.push('Cadence streams use the normal event send path. Their synthetic distinct_id values are never people counts or identity-model evidence.', '');
|
|
56
|
+
for (const stream of standaloneEvents) {
|
|
57
|
+
const dimensions = Object.entries(stream.dimensions || {}).map(([key, values]) => `${key} (${values.length} values)`);
|
|
58
|
+
const properties = Object.keys(stream.properties || {});
|
|
59
|
+
const identity = stream.distinctIdFrom ? `dimension ${stream.distinctIdFrom}` : 'event name';
|
|
60
|
+
parts.push(`- ${stream.event}: cadence ${stream.cadence ?? 'day'}; dimensions ${contextList(dimensions)}; properties ${contextList(properties)}; synthetic distinct_id from ${identity}`);
|
|
61
|
+
}
|
|
62
|
+
parts.push('');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const warehouseMetrics = config.warehouseMetrics || [];
|
|
66
|
+
if (warehouseMetrics.length) {
|
|
67
|
+
parts.push(`### Warehouse metrics (${warehouseMetrics.length})`);
|
|
68
|
+
parts.push('Sum adds buckets; LastValue reads the latest snapshot.', '');
|
|
69
|
+
parts.push('Separate /warehouse-metrics deployment is required; provisioning and sendToMixpanel do not load warehouse tables or save warehouse metrics.', '');
|
|
70
|
+
for (const metric of warehouseMetrics) {
|
|
71
|
+
const type = metric.type ?? 'additive';
|
|
72
|
+
const source = metric.source;
|
|
73
|
+
const aggregation = type === 'point-in-time' ? 'LastValue' : 'Sum';
|
|
74
|
+
parts.push(`- ${metric.name}: type ${type}; grain ${metric.grain ?? 'day'}; history ${metric.history ?? 0} periods before the event window; sparse ${metric.sparse ?? false}; Mixpanel aggregation ${aggregation}`);
|
|
75
|
+
if (type === 'point-in-time') parts.push(` baseline ${metric.baseline ?? 0} (point-in-time starting level)`);
|
|
76
|
+
parts.push(` source events ${contextList(source.event)}; minus ${contextList(source.minus)}; measure ${source.measure ?? 'count'}; property ${source.property ?? '(none)'}; where ${source.where ? 'present (function, not evaluated)' : '(none)'}`);
|
|
77
|
+
parts.push(` columns: time ${metric.timeColumn ?? 'date'}; value ${metric.valueColumn ?? 'value'}; groupBy ${contextList(source.groupBy)}; extra ${contextList(Object.keys(metric.columns || {}))}`);
|
|
78
|
+
}
|
|
79
|
+
parts.push('');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let md = parts.join('\n');
|
|
83
|
+
if (md.length > 50000) md = md.slice(0, 49900) + '\n\n…(truncated to 50,000 chars)';
|
|
84
|
+
return md;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function contextList(value) {
|
|
88
|
+
return (Array.isArray(value) ? value.join(', ') : value) || '(none)';
|
|
89
|
+
}
|
|
@@ -32,6 +32,7 @@ import path, { dirname, resolve } from 'path';
|
|
|
32
32
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
33
33
|
import dotenv from 'dotenv';
|
|
34
34
|
import { loadFromFile, extractComments } from '../../../index.js';
|
|
35
|
+
import { buildContext } from './context.mjs';
|
|
35
36
|
|
|
36
37
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
37
38
|
const REPO_ROOT = resolve(__dirname, '../../../');
|
|
@@ -204,66 +205,6 @@ function isoInDays(days) {
|
|
|
204
205
|
return new Date(ms).toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
function buildContext(name, config, comments, groupKeys, stories) {
|
|
208
|
-
const parts = [`# ${name}`, ''];
|
|
209
|
-
if (comments.overview) parts.push(comments.overview, '');
|
|
210
|
-
|
|
211
|
-
// Preferred source: the stories export — each story's narrative is the
|
|
212
|
-
// human-readable behavior and mixpanelReport (free-form object) points at
|
|
213
|
-
// the report where it shows. Comment scrape is the fallback for dungeons
|
|
214
|
-
// without stories (schema-only or pre-1.6).
|
|
215
|
-
if (stories?.length) {
|
|
216
|
-
parts.push('## Engineered Behaviors', '');
|
|
217
|
-
parts.push(`${stories.length} machine-verified story patterns (from the dungeon's \`stories\` export):`, '');
|
|
218
|
-
for (const s of stories) {
|
|
219
|
-
const arch = s.archetype ? ` — ${s.archetype}` : '';
|
|
220
|
-
parts.push(`### ${s.id}${arch}`, '');
|
|
221
|
-
if (s.narrative) parts.push(String(s.narrative).trim(), '');
|
|
222
|
-
if (s.mixpanelReport && typeof s.mixpanelReport === 'object') {
|
|
223
|
-
parts.push('How to see it in Mixpanel:');
|
|
224
|
-
for (const [k, v] of Object.entries(s.mixpanelReport)) {
|
|
225
|
-
parts.push(`- **${k}**: ${typeof v === 'string' ? v : JSON.stringify(v)}`);
|
|
226
|
-
}
|
|
227
|
-
parts.push('');
|
|
228
|
-
}
|
|
229
|
-
if (Array.isArray(s.intentionalDeviations) && s.intentionalDeviations.length) {
|
|
230
|
-
parts.push('Notes:', ...s.intentionalDeviations.map((d) => `- ${d}`), '');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
} else if (comments.hookStories) {
|
|
234
|
-
parts.push('## Engineered Behaviors', '', comments.hookStories, '');
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
parts.push('## Schema', '');
|
|
238
|
-
const events = config.events || [];
|
|
239
|
-
parts.push(`### Events (${events.length})`);
|
|
240
|
-
for (const e of events) {
|
|
241
|
-
const props = e.properties ? Object.keys(e.properties).join(', ') : '';
|
|
242
|
-
const weight = e.weight != null ? ` (weight ${e.weight})` : '';
|
|
243
|
-
parts.push(`- ${e.event}${weight}${props ? ` — ${props}` : ''}`);
|
|
244
|
-
}
|
|
245
|
-
parts.push('');
|
|
246
|
-
|
|
247
|
-
const funnels = config.funnels || [];
|
|
248
|
-
if (funnels.length) {
|
|
249
|
-
parts.push(`### Funnels (${funnels.length})`);
|
|
250
|
-
for (const f of funnels) {
|
|
251
|
-
const seq = (f.sequence || []).join(' → ');
|
|
252
|
-
const rate = f.conversionRate != null ? ` (${f.conversionRate}%)` : '';
|
|
253
|
-
parts.push(`- ${f.name || '(unnamed)'}: ${seq}${rate}`);
|
|
254
|
-
}
|
|
255
|
-
parts.push('');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (groupKeys.length) {
|
|
259
|
-
parts.push('### Group keys', ...groupKeys.map((g) => `- ${g.property_name} (${g.display_name})`), '');
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
let md = parts.join('\n');
|
|
263
|
-
if (md.length > 50000) md = md.slice(0, 49900) + '\n\n…(truncated to 50,000 chars)';
|
|
264
|
-
return md;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
208
|
function writeBackCredentials(p, creds) {
|
|
268
209
|
let src = readFileSync(p, 'utf-8');
|
|
269
210
|
const block =
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: headless-build
|
|
3
3
|
description: Use after a dungeon's data is loaded into a real Mixpanel project — builds the full demoable environment with mixpanel-headless (dashboards, charts, Lexicon, cohorts, custom properties, behaviors/metrics/formulas, annotations) targeted at that dungeon's engineered stories, then verifies the stories still read live. Final step after create-dungeon / write-hooks / verify-dungeon / create-project.
|
|
4
|
-
argument-hint: [dungeon path, e.g. dungeons/user/nyc-dcp/nyc-dcp.js]
|
|
4
|
+
argument-hint: '[dungeon path, e.g. dungeons/user/nyc-dcp/nyc-dcp.js]'
|
|
5
5
|
model: claude-opus-4-6
|
|
6
6
|
effort: max
|
|
7
7
|
---
|
|
@@ -14,6 +14,23 @@ Last step of the pipeline:
|
|
|
14
14
|
/create-dungeon → /write-hooks → /verify-dungeon → /create-project → /headless-build
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
For `warehouseMetrics`, insert `/warehouse-metrics` after verification and project
|
|
18
|
+
provisioning, before this build. Use the verified local disk artifact prefix.
|
|
19
|
+
Ordinary event import loads `standaloneEvents` but does not deploy warehouse tables.
|
|
20
|
+
|
|
21
|
+
Keep cadence and warehouse metrics separate from people analyses. Synthetic
|
|
22
|
+
standalone `distinct_id` values identify series; never count them as users or
|
|
23
|
+
include them in funnels, retention, or identity stitching. Build aggregate reports
|
|
24
|
+
from their declared numeric properties and dimensions.
|
|
25
|
+
|
|
26
|
+
For warehouse-backed charts, read the manifest and saved metric ids from the
|
|
27
|
+
warehouse handoff. `history` may extend the metric window before the user-event
|
|
28
|
+
window; preserve that range and label synthetic backfill. Check saved definitions
|
|
29
|
+
and `previewWarehouseMetric` results before narrating values. A successful
|
|
30
|
+
`refreshWarehouseMetric` only invalidates cache; it does not execute the query.
|
|
31
|
+
Use the existing `/macro/setup-bq-warehouse` flow through `/warehouse-metrics`
|
|
32
|
+
for source setup. Do not reimplement source creation or IAM grants in build code.
|
|
33
|
+
|
|
17
34
|
The project already has data. This skill builds everything a human sees: themed
|
|
18
35
|
dashboards whose narrative is computed from the live data, an annotated Lexicon,
|
|
19
36
|
saved cohorts and custom properties, saved behaviors/metrics/formulas, and
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: powertools
|
|
3
3
|
description: Use when any task needs the Mixpanel Power Tools API ("use powertools") — schema export (get-schema), event volumes, project CRUD, query methods, macros, or snapshotting a prod project's schema to copy it into a dungeon. Companion to create-project (which handles provisioning specifically).
|
|
4
|
-
argument-hint: [what to do, e.g. "get schema for project 12345" or "copy project 12345 into a dungeon"]
|
|
4
|
+
argument-hint: '[what to do, e.g. "get schema for project 12345" or "copy project 12345 into a dungeon"]'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Power Tools API
|
|
@@ -43,6 +43,25 @@ node .claude/skills/powertools/snapshot-project.mjs <project_id> --bearer <token
|
|
|
43
43
|
|
|
44
44
|
## Endpoint catalog (the useful subset)
|
|
45
45
|
|
|
46
|
+
### Warehouse handoff (v1.8.0)
|
|
47
|
+
|
|
48
|
+
For a dungeon with `warehouseMetrics`, use `/warehouse-metrics` after local
|
|
49
|
+
verification produces disk tables and a manifest, and `/create-project` writes
|
|
50
|
+
the project id. Pass the explicit verified `--data-prefix`; preserve those files.
|
|
51
|
+
That skill owns load, source setup, SQL preview, and metric create-or-skip.
|
|
52
|
+
Reuse `/macro/setup-bq-warehouse` through `pt.mjs`; do not reimplement its
|
|
53
|
+
source creation, GCP IAM grant, or dataset ACL handling here.
|
|
54
|
+
|
|
55
|
+
Warehouse metric endpoints include `getWarehouseMetrics`, `previewWarehouseMetric`,
|
|
56
|
+
`createWarehouseMetric`, and `refreshWarehouseMetric` in the `/crud` family.
|
|
57
|
+
Read endpoint docs before live use. Preview executes the read query; create does
|
|
58
|
+
not validate SQL, and refresh only invalidates cache. A docs-route 404 uses the
|
|
59
|
+
warehouse skill's manual-setup fallback; other errors must surface.
|
|
60
|
+
|
|
61
|
+
`standaloneEvents` imports as a separate event stream through the ordinary sender.
|
|
62
|
+
Its synthetic ids are series keys, so schema snapshots and dashboard counts must
|
|
63
|
+
distinguish cadence telemetry from user `events[]`. It is not a warehouse source.
|
|
64
|
+
|
|
46
65
|
GET the path for full docs. Full list: GET `/` and GET `/macro`.
|
|
47
66
|
|
|
48
67
|
**crud** — 187 endpoints. GET `/crud` for the full list.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-check
|
|
3
|
+
description: "Use before a dungeon-master release to audit versioned docs, skills, tests, determinism, package contents, and Git state; report release blockers and prepare an explicitly authorized PR and merge handoff."
|
|
4
|
+
argument-hint: "[version, e.g. 1.8.0] [optional base branch]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Release check
|
|
8
|
+
|
|
9
|
+
Check the current checkout before release. Read `AGENTS.md`, `package.json`,
|
|
10
|
+
the target changelog entry, and its upgrade guide. Default to validation only.
|
|
11
|
+
Never publish to npm unless the user gives explicit authorization for npm publishing.
|
|
12
|
+
An instruction to merge a PR is not permission to publish a package.
|
|
13
|
+
|
|
14
|
+
## Establish the release scope
|
|
15
|
+
|
|
16
|
+
1. Inspect `git status --short`, the current branch, remotes, and recent history.
|
|
17
|
+
Preserve unrelated changes. Never reset or clean the worktree to make checks pass.
|
|
18
|
+
2. Confirm the requested version matches `package.json`, the lockfile, changelog,
|
|
19
|
+
and guide. Do not invent a release date or bump a version without authorization.
|
|
20
|
+
3. Compare the release diff against the base branch. Check exports, dependencies,
|
|
21
|
+
config defaults, compatibility notes, examples, and migration instructions.
|
|
22
|
+
4. Inspect test setup before running it. This repo's suite prunes `data/` and `tmp/`.
|
|
23
|
+
If pending deployment artifacts exist, preserve them outside those directories
|
|
24
|
+
or obtain consent before running the suite. Do not silently delete verified inputs.
|
|
25
|
+
|
|
26
|
+
## Run executable gates
|
|
27
|
+
|
|
28
|
+
Use `set -o pipefail` for every piped command. Keep all Vitest output behind
|
|
29
|
+
`2>&1 | tail -50`; a successful tail command alone is not passing evidence.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
set -o pipefail
|
|
33
|
+
npm test 2>&1 | tail -50
|
|
34
|
+
npm run typecheck
|
|
35
|
+
git diff --check
|
|
36
|
+
npx vitest run tests/unit/engine-shape-canary.test.js 2>&1 | tail -50
|
|
37
|
+
RUN_FULL_SWEEP=1 npx vitest run tests/e2e/engine-shape-full-sweep.test.js 2>&1 | tail -50
|
|
38
|
+
node tests/engine/smoke-test-all.mjs
|
|
39
|
+
npm pack --dry-run --json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Run commands separately or chain with `&&` so a failure cannot disappear behind
|
|
43
|
+
a later success. Record exit codes, pass/skip counts, and the tested commit/diff.
|
|
44
|
+
The smoke script discovers vertical dungeons only. Verify changed technical or
|
|
45
|
+
customer fixtures separately with the existing verification runners.
|
|
46
|
+
|
|
47
|
+
For generation changes, require the event-stream determinism test. Compare runs
|
|
48
|
+
with the same seed, pinned window, and `concurrency: 1`; strip only `insert_id`.
|
|
49
|
+
Use sequential test cases because the RNG is shared. Include warehouse noise in
|
|
50
|
+
warehouse determinism checks. Never accept an unrun or failing determinism gate.
|
|
51
|
+
|
|
52
|
+
## Audit docs and skills
|
|
53
|
+
|
|
54
|
+
- Run `tests/e2e/skills-contract.test.js`. Parse YAML frontmatter rather than
|
|
55
|
+
guessing from appearance: names match folders, descriptions are strings, and
|
|
56
|
+
`argument-hint` is a quoted string.
|
|
57
|
+
- The canonical skills live in `.claude/skills`. Verify `.agents/skills` and
|
|
58
|
+
`.github/skills` resolve to that directory. Do not duplicate skill content.
|
|
59
|
+
- Check README, HOOKS, type comments, changelog, and guide against actual code.
|
|
60
|
+
Do not carry stale test counts, old skill paths, or historical operational
|
|
61
|
+
failures forward as current release claims.
|
|
62
|
+
- Follow the complete handoff: author, optional hooks, verify, provision,
|
|
63
|
+
generate/import, optional `/warehouse-metrics`, then headless build.
|
|
64
|
+
- Distinguish user events, identity-less `standaloneEvents`, and warehouse rows.
|
|
65
|
+
Synthetic IDs are not people. Standalone hooks return retained records;
|
|
66
|
+
warehouse hooks mutate rows and ignore returns. Preserve deployment artifacts.
|
|
67
|
+
- If API integrations changed, read the current endpoint docs and local handback.
|
|
68
|
+
Distinguish docs discovery, mocked execution, dry-run, and live verification.
|
|
69
|
+
Live writes require explicit authorization. Never claim an endpoint GET proves
|
|
70
|
+
authenticated write permission or end-to-end deployment.
|
|
71
|
+
|
|
72
|
+
## Inspect the package and internal files
|
|
73
|
+
|
|
74
|
+
Read the file list from `npm pack --dry-run --json`. Confirm new runtime modules,
|
|
75
|
+
dependencies, scripts, and canonical skill files ship. Reject credentials,
|
|
76
|
+
customer data, `plans/`, `research/`, `.superpowers/`, or obsolete skill paths.
|
|
77
|
+
Check symlink handling in the package separately from workspace discovery.
|
|
78
|
+
|
|
79
|
+
Verify `git ls-files .superpowers` is empty and `.gitignore` covers the directory.
|
|
80
|
+
Ignoring an already tracked file does not untrack it. Obtain authorization before
|
|
81
|
+
removing index entries; preserve local reports. Archive completed plan folders
|
|
82
|
+
whole under `plans/archived/`; leave active or ambiguous work in place.
|
|
83
|
+
|
|
84
|
+
## Report and optional shipping handoff
|
|
85
|
+
|
|
86
|
+
Report blockers first, then evidence, skipped checks, compatibility changes,
|
|
87
|
+
and remaining operational limits. Do not fix unrelated failures or weaken tests.
|
|
88
|
+
|
|
89
|
+
Only when explicitly requested: inspect all staged files for secrets, commit the
|
|
90
|
+
authorized changes, push the feature branch, create or reuse its PR, and inspect
|
|
91
|
+
CI and mergeability. Require successful completed checks for the current PR head
|
|
92
|
+
before squash merge. If no CI checks are configured, report that explicitly and
|
|
93
|
+
use recorded local release gates; do not describe absent CI as passing. A failed
|
|
94
|
+
release gate requires investigation and an explicit operator decision before shipping.
|
|
95
|
+
Never bypass protections or use admin merge
|
|
96
|
+
to hide a failing check. Confirm the PR is merged before switching local branches.
|
|
97
|
+
Fetch, switch to `main`, and pull with `--ff-only`; stop rather than discard local
|
|
98
|
+
changes or divergent commits. Report the PR URL, merge SHA, current branch, and
|
|
99
|
+
working-tree status. Leave npm publishing to the operator unless separately authorized.
|