Files
GearBox/.planning/quick/260406-j44-comprehensive-dev-seed-script-for-bikepa/260406-j44-PLAN.md

11 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
quick 260406-j44 execute 1
src/db/dev-seed.ts
src/db/dev-seed-data.ts
package.json
true
truths artifacts key_links
Running `bun run db:seed:dev` populates the database with realistic bikepacking gear data
Re-running the script on a populated DB is safe (idempotent — skips if data exists)
All foreign key relationships are valid (items reference real categories, setup_items reference real items, etc.)
path provides
src/db/dev-seed-data.ts All seed data as typed constants
path provides
src/db/dev-seed.ts Idempotent seed runner
path provides
package.json db:seed:dev script entry
from to via pattern
src/db/dev-seed.ts src/db/schema.ts drizzle insert operations db.insert(schema.
from to via pattern
src/db/dev-seed.ts src/db/dev-seed-data.ts import data constants import.*from.*dev-seed-data
Create a comprehensive development seed script that populates PostgreSQL with realistic bikepacking/outdoor gear data for local development.

Purpose: Enable developers to quickly stand up a fully-populated dev environment with realistic data spanning all entity types — global items, tags, user collections, threads, setups, and settings.

Output: Two new files (dev-seed-data.ts for data, dev-seed.ts for the runner) and a db:seed:dev npm script.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@CLAUDE.md @src/db/schema.ts @src/db/seed-global-items.ts @src/db/global-items-seed.json @tests/helpers/db.ts @src/db/index.ts

From src/db/schema.ts:

  • users: { id (serial PK), logtoSub (text, unique), displayName (text), avatarUrl (text), bio (text) }
  • categories: { id (serial PK), name (text), icon (text), userId (int FK users) } — unique(userId, name)
  • items: { id (serial PK), name, weightGrams, priceCents, categoryId (FK), userId (FK), notes, productUrl, imageFilename, imageSourceUrl, quantity (default 1), globalItemId (FK nullable), purchasePriceCents }
  • threads: { id (serial PK), name, status (default "active"), resolvedCandidateId, categoryId (FK), userId (FK) }
  • threadCandidates: { id (serial PK), threadId (FK cascade), name, weightGrams, priceCents, categoryId (FK), notes, productUrl, imageFilename, imageSourceUrl, status (default "researching"), pros, cons, sortOrder, globalItemId (FK nullable) }
  • setups: { id (serial PK), name, userId (FK), isPublic (default false) }
  • setupItems: { id (serial PK), setupId (FK cascade), itemId (FK cascade), classification (default "base") }
  • globalItems: { id (serial PK), brand, model, category, weightGrams, priceCents, imageUrl, description }
  • tags: { id (serial PK), name (unique) }
  • globalItemTags: { globalItemId (FK cascade), tagId (FK cascade) } — composite PK
  • settings: { userId (FK), key (text), value (text) } — composite PK(userId, key)

From src/db/index.ts:

  • db: drizzle instance connected to PostgreSQL via DATABASE_URL

From src/db/seed-global-items.ts (idempotent pattern to follow):

  • Check existing.length > 0 before inserting
  • Accept optional db parameter with production db as default
Task 1: Create seed data constants src/db/dev-seed-data.ts Create `src/db/dev-seed-data.ts` exporting typed constant arrays for all seed data. Keep data and logic separate for maintainability.

Categories (8-10 with icons):

  • Bags (briefcase), Shelter (tent), Sleep System (moon), Cooking (flame), Lighting (flashlight), Tools & Repair (wrench), Clothing (shirt), Water (droplets), Electronics (battery), Navigation (compass)

Global Items (35-45 items spanning categories). Use real bikepacking gear brands/models with realistic weights and prices. Examples per category:

  • Bags: Revelate Designs Terrapin, Apidura Expedition Handlebar Pack, Ortlieb Frame-Pack, Rockgeist BarJam, Oveja Negra Superwedgie
  • Shelter: Zpacks Duplex, Tarptent Stratospire, Durston X-Mid 1, Big Agnes Copper Spur HV UL1
  • Sleep: Enlightened Equipment Enigma 20, Therm-a-Rest NeoAir XLite, Nemo Tensor Insulated, Sea to Summit Aeros Pillow
  • Cooking: BRS-3000T, Soto Windmaster, Toaks 750ml, Snow Peak Ti-Mini Solo
  • Lighting: Nitecore NU25, Lezyne Lite Drive, Fenix HL60R
  • Tools: Park Tool IB-3, Lezyne CNC Chain Breaker, Gorilla Tape mini roll
  • Clothing: Patagonia R1 Air, Frogg Toggs UL2 Rain Suit, Buff Merino Wool
  • Water: Sawyer Squeeze, Katadyn BeFree, HydraPak Seeker 2L
  • Electronics: Anker 10000 PD, Garmin inReach Mini 2
  • Navigation: Wahoo ELEMNT BOLT, Caltopo printed maps holder

Each global item: { brand, model, category, weightGrams, priceCents, description } — use integers for priceCents (e.g., 67900 for $679.00).

Tags (reuse existing SEED_TAGS from seed-global-items.ts plus a few more):

  • Existing tags are already seeded by seedTags(), so do NOT re-seed them.

Tag assignments — array of { globalItemIndex, tagNames } mapping global items to tags by index position and tag name. Example: index 0 (Terrapin) gets ["saddlebag", "waterproof", "bikepacking"]. Assign 2-4 tags per global item.

User items (15-20 items for the dev user's collection):

  • ~10 items linked to global items via globalItemId (reference items). Use the index into the global items array. Include purchasePriceCents for some (actual price paid, sometimes different from MSRP).
  • ~5-7 standalone items with no globalItemId (custom/unique gear the user added manually)
  • Spread across categories. Include notes on some, quantity > 1 on a couple.

Threads (3 threads):

  1. Active thread "Handlebar Bag Upgrade" in Bags category with 3 candidates (some catalog-linked via globalItemId index), statuses: researching/shortlisted/researching
  2. Active thread "Navigation Computer" in Electronics with 2 candidates
  3. Resolved thread "Camp Stove" in Cooking with 2 candidates, one marked "arrived"

Setups (2 setups):

  1. "Weekend Overnighter" — 6-8 items from the user's collection, mix of base/worn/consumable classifications, isPublic: true
  2. "Ultra-Light Day Ride" — 3-4 items, isPublic: false

Settings: weightUnit "g", currency "EUR"

Export everything as named constants with proper TypeScript types (using as const where appropriate for literal inference). bunx tsc --noEmit src/db/dev-seed-data.ts 2>&1 | head -20 All seed data constants exported with proper types, no TypeScript errors

Task 2: Create seed runner and wire npm script src/db/dev-seed.ts, package.json Create `src/db/dev-seed.ts` — the idempotent seed runner.

Structure:

  1. Import db from ./index.ts, all schema tables from ./schema.ts, all data from ./dev-seed-data.ts, and seedGlobalItems from ./seed-global-items.ts
  2. Main seedDevData() async function:

Idempotency check: Query users table for a user with logtoSub = "dev-user-seed". If found, log "Dev seed data already exists, skipping." and return early. This single check gates the entire script since all other data depends on this user.

Insertion order (respects FK constraints):

  1. Call seedGlobalItems(db) first — reuse existing function to populate global_items and tags
  2. Insert dev user: { logtoSub: "dev-user-seed", displayName: "Dev User", bio: "Bikepacking enthusiast" } — capture returned user for userId FK
  3. Insert categories (with userId from step 2) — capture returned rows for categoryId mapping
  4. Look up tag IDs by name from tags table (they were seeded in step 1) — build tagNameToId map
  5. Insert global item tag assignments using globalItemTags table — use the global item IDs from step 1 and tag IDs from step 4. Note: seedGlobalItems does not return IDs, so query globalItems table to get IDs by brand+model matching after seeding.
  6. Insert user items (with userId, categoryId, globalItemId references) — capture returned rows for setup_items
  7. Insert threads (with userId, categoryId) — capture returned IDs
  8. Insert thread candidates (with threadId, categoryId, globalItemId) — for the resolved thread, set resolvedCandidateId on the thread after inserting candidates
  9. Insert setups (with userId) — capture returned IDs
  10. Insert setup_items (with setupId, itemId references from step 6)
  11. Insert settings (with userId)
  12. Log summary: "Dev seed complete: X global items, X tags, X user items, X threads, X setups"

Script entry point: At bottom of file:

seedDevData()
  .then(() => process.exit(0))
  .catch((err) => { console.error("Seed failed:", err); process.exit(1); });

package.json: Add "db:seed:dev": "bun run src/db/dev-seed.ts" to scripts section.

Use db.insert(...).values(...).returning() to capture IDs needed for FK references. Use db.insert(...).values(...) (no returning) for leaf tables.

Wrap everything in a try/catch. On error, log the error and re-throw. Do NOT use transactions (PostgreSQL serial IDs and the idempotency check make this safe enough for dev seeding). bunx tsc --noEmit src/db/dev-seed.ts 2>&1 | head -20 - bun run db:seed:dev executes without error on a fresh (migrated) database - Re-running logs "already exists, skipping" and exits cleanly - Database contains: 1 dev user, 8+ categories, 35+ global items with tag assignments, 15+ user items (some catalog-linked), 3 threads with candidates, 2 setups with items, settings

1. `bunx tsc --noEmit src/db/dev-seed-data.ts src/db/dev-seed.ts` — no type errors 2. `bun run db:seed:dev` — completes without error (requires running PostgreSQL) 3. Re-run `bun run db:seed:dev` — logs skip message, exits 0

<success_criteria>

  • Script populates all entity types: users, categories, global items, tags, global item tag links, items (reference + standalone), threads, candidates (some catalog-linked), setups, setup_items, settings
  • Idempotent: safe to run multiple times
  • Data is realistic bikepacking gear with accurate weights/prices
  • Foreign key relationships are all valid
  • bun run db:seed:dev is the only command needed </success_criteria>
After completion, create `.planning/quick/260406-j44-comprehensive-dev-seed-script-for-bikepa/260406-j44-SUMMARY.md`