Skip to content

Commit 31ffc0a

Browse files
feat(dev-seed): improve local seed scripts (#3058)
1 parent aeba4d4 commit 31ffc0a

10 files changed

Lines changed: 1146 additions & 33 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,5 @@ supabase/.temp
107107
TMP_CI_commit_msg.txt
108108
*.csv
109109
run-milvus-test.sh
110-
.env*.local
110+
111+
.beads/

dev/seed/AGENTS.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# AGENTS.md — `dev/seed/`
2+
3+
Contract for `pnpm dev:seed` runner/topic modules. Read before adding, modifying, or invoking seeds.
4+
5+
## Layout
6+
7+
```
8+
dev/seed/
9+
index.ts Runner: args, listing, result output.
10+
lib/
11+
preflight.ts Import FIRST; mutates process.env from argv.
12+
db.ts Lazy drizzle client.
13+
stripe.ts Lazy Stripe test-mode client/customer helpers.
14+
<scope>/<topic>.ts Topic module. Scope = folder; topic = filename.
15+
```
16+
17+
Runner globs `<scope>/*.ts`. Any top-level dir except `lib/` is a scope. Scope folders must contain only topic files; shared code goes in `lib/`.
18+
19+
## Invocation
20+
21+
```sh
22+
pnpm dev:seed <scope>:<topic> [args...] # canonical
23+
pnpm dev:seed <scope> <topic> [args...] # accepted
24+
pnpm dev:seed # list topics + usage
25+
```
26+
27+
Global flag:
28+
29+
- `--json`: suppress topic stdout; print exactly one `JSON.stringify(result)` line. Use `pnpm -s` for clean pipes:
30+
```sh
31+
USER_ID=$(pnpm -s dev:seed app:create-user "Foo" foo@example.com --json | jq -r .userId)
32+
```
33+
Implementation: `preflight.ts` sets `DOTENV_CONFIG_QUIET=true` before other imports; runner suppresses `console.log/info/warn` during `run()`. Do not bypass with `process.stdout.write`.
34+
35+
## Topic contract
36+
37+
Topic files MUST:
38+
39+
- Export `run(...args: string[]): Promise<SeedResult | void> | SeedResult | void`.
40+
- `type SeedResult = Record<string, string | number | boolean | null>`: flat JSON primitives only; no nested objects; stringify Dates.
41+
- Return every id/email/handle/balance needed by follow-up commands; the runner formats all output from this object.
42+
- Support `--help`/`-h` via local `printUsage()` and early return.
43+
- Reset only their own data at start for idempotent reruns. Delete by stable ids/emails, stable sandbox prefixes, or `dev-seed:` category prefixes.
44+
- Avoid module-level side effects (DB writes/network); no-args listing imports modules.
45+
46+
Topic files SHOULD:
47+
48+
- Export `const usage = '<arg> <arg> [options]'`; omit if no args.
49+
- Print short context before returning (`This fixture represents:`, `Note:`, `Suggested next step:`). Runner appends the Result block.
50+
51+
Topic files MUST NOT:
52+
53+
- Write stdout in `--json` beyond the runner's single JSON line; `console.*` is suppressed during `run()`, but `process.stdout.write` is not.
54+
- Print completion blocks or `key: value` lines duplicating `SeedResult`.
55+
- Use `as` casts or `!` assertions; prefer `satisfies typeof <table>.$inferInsert`.
56+
- Import from `apps/web/src/...` (server-only/Next runtime); replicate minimal logic in `lib/`.
57+
- Return nested objects or non-primitives in `SeedResult`; flatten with prefixed keys (`eligibleUserId`, `eligibleInstanceId`, ...).
58+
59+
## Helpers
60+
61+
- `getSeedDb()`: lazy singleton drizzle client, safe from any topic; pool cap 1.
62+
- `closeSeedDb()`: runner calls in `finally`; topics should not.
63+
- `createSeedStripeCustomer({ email, name, kiloUserId })`: creates real Stripe test-mode customer with `metadata: { kiloUserId, source: 'dev-seed' }`.
64+
- `deleteSeedStripeCustomer(id)`: rollback helper; swallows "no such customer".
65+
- `lib/stripe.ts` rejects missing/non-`sk_test_...` `STRIPE_SECRET_KEY`.
66+
- For seeded users used by Stripe-touching app code (`/profile`, billing pages, KiloClaw subscriptions), create a real Stripe customer. Never use `cus_seed_...`: it causes `StripeInvalidRequestError: No such customer` 400s. Order matches `createUserOnSignIn`: create Stripe customer, insert DB row, delete Stripe customer in `catch` on insert failure.
67+
68+
## Direct user inserts
69+
70+
Bare `kilocode_users` inserts leave users trapped in onboarding. To reach product surfaces, set:
71+
72+
- `has_validation_stytch: true` — bypasses `/account-verification` (`!== null`).
73+
- `customer_source: 'dev-seed'` — bypasses `/customer-source-survey` (`!== null`; `''` means skipped).
74+
75+
Canonical example: `app/create-user.ts`. Gate code: `apps/web/src/lib/stytch.ts` (`getStytchStatus`) and `apps/web/src/lib/survey-redirect.ts` (`maybeInterceptWithSurvey`). When adding a gate, update this section and `app/create-user.ts` together.
76+
77+
## New-topic checklist
78+
79+
1. Add `<scope>/<topic>.ts` (new top-level dirs become scopes).
80+
2. Export `run()` and usually `usage`.
81+
3. Use `getSeedDb()`; use `createSeedStripeCustomer` for app users needing Stripe.
82+
4. Reset only own fixtures idempotently.
83+
5. Return flat `SeedResult` with every follow-up id.
84+
6. Verify both modes:
85+
```sh
86+
pnpm dev:seed <scope>:<topic> <args> # human-readable Result block
87+
pnpm -s dev:seed <scope>:<topic> <args> --json # single JSON line
88+
```
89+
7. Run `pnpm format` and `pnpm lint`. Skip `pnpm typecheck`: `dev/seed/` runs via `tsx` and is outside tsconfig; runtime is the type check.

0 commit comments

Comments
 (0)