--- phase: 18-global-items-public-profiles plan: 01 type: execute wave: 1 depends_on: [] files_modified: - src/db/schema.ts - src/shared/schemas.ts - src/shared/types.ts - src/db/global-items-seed.json autonomous: true requirements: [GLOB-01, GLOB-02, PROF-01, PROF-03] must_haves: truths: - "globalItems table exists with brand, model, category, weightGrams, priceCents, imageUrl, description, createdAt columns" - "itemGlobalLinks junction table exists linking items to globalItems" - "users table has displayName, avatarUrl, bio nullable columns" - "setups table has isPublic boolean column defaulting to false" - "Zod schemas exist for global item search, item linking, profile update, and setup visibility" - "Types are inferred from Zod schemas and Drizzle tables, not manually duplicated" artifacts: - path: "src/db/schema.ts" provides: "globalItems, itemGlobalLinks tables + users profile cols + setups isPublic" contains: "globalItems" - path: "src/shared/schemas.ts" provides: "searchGlobalItemsSchema, linkItemSchema, updateProfileSchema" contains: "searchGlobalItemsSchema" - path: "src/shared/types.ts" provides: "GlobalItem, ItemGlobalLink, UpdateProfile, LinkItem types" contains: "GlobalItem" - path: "src/db/global-items-seed.json" provides: "Initial bikepacking gear catalog seed data" min_lines: 20 key_links: - from: "src/shared/types.ts" to: "src/db/schema.ts" via: "Drizzle $inferSelect" pattern: "globalItems\\.\\$inferSelect" - from: "src/shared/types.ts" to: "src/shared/schemas.ts" via: "Zod z.infer" pattern: "z\\.infer.*updateProfileSchema" --- Define all schema foundations for Phase 18: new database tables (globalItems, itemGlobalLinks), column additions to users (profile fields) and setups (isPublic), Zod validation schemas, TypeScript types, and seed data file. Purpose: Every subsequent plan depends on these schema definitions. Defining contracts first prevents the scavenger hunt anti-pattern. Output: Updated schema.ts, schemas.ts, types.ts, and global-items-seed.json @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/18-global-items-public-profiles/18-CONTEXT.md @.planning/phases/18-global-items-public-profiles/18-RESEARCH.md @src/db/schema.ts @src/shared/schemas.ts @src/shared/types.ts Task 1: Schema tables and column additions src/db/schema.ts src/db/schema.ts Add `boolean` to the drizzle-orm/pg-core imports (per D-01, D-12). Add the `globalItems` table per D-01: - `id: serial("id").primaryKey()` - `brand: text("brand").notNull()` - `model: text("model").notNull()` - `category: text("category")` - `weightGrams: doublePrecision("weight_grams")` - `priceCents: integer("price_cents")` - `imageUrl: text("image_url")` - `description: text("description")` - `createdAt: timestamp("created_at").defaultNow().notNull()` Add the `itemGlobalLinks` junction table per D-02: - `id: serial("id").primaryKey()` - `itemId: integer("item_id").notNull().references(() => items.id, { onDelete: "cascade" }).unique()` — each user item links to at most one global item - `globalItemId: integer("global_item_id").notNull().references(() => globalItems.id, { onDelete: "cascade" })` Extend the `users` table per D-08 — add three nullable text columns: - `displayName: text("display_name")` - `avatarUrl: text("avatar_url")` - `bio: text("bio")` Extend the `setups` table per D-12 — add: - `isPublic: boolean("is_public").notNull().default(false)` Place new tables after setupItems section. Export all new tables. After schema changes, run `bun run db:generate` to create the migration, then `bun run db:push` to verify it applies cleanly. bun run db:generate 2>&1 | tail -5 - grep -q "globalItems" src/db/schema.ts - grep -q "itemGlobalLinks" src/db/schema.ts - grep -q "displayName" src/db/schema.ts - grep -q "isPublic" src/db/schema.ts - grep -q "boolean" src/db/schema.ts All four schema additions (globalItems table, itemGlobalLinks table, users profile columns, setups isPublic) are defined and exported. Migration generated successfully. Task 2: Zod schemas, types, and seed data src/shared/schemas.ts, src/shared/types.ts, src/db/global-items-seed.json src/shared/schemas.ts, src/shared/types.ts **schemas.ts** — Add the following Zod schemas at the end of the file: 1. `searchGlobalItemsSchema` per D-04 and D-16: ``` z.object({ q: z.string().optional() }) ``` 2. `linkItemSchema` per D-18: ``` z.object({ globalItemId: z.number().int().positive() }) ``` 3. `updateProfileSchema` per D-08, D-21: ``` z.object({ displayName: z.string().max(100).optional(), avatarUrl: z.string().optional(), bio: z.string().max(500).optional(), }) ``` 4. Update the existing `updateSetupSchema` to include `isPublic` per D-12, D-14: Add `isPublic: z.boolean().optional()` to the existing schema object. 5. Update the existing `createSetupSchema` if it exists — add `isPublic: z.boolean().optional().default(false)`. **types.ts** — Add type exports: - `export type GlobalItem = typeof globalItems.$inferSelect;` (import globalItems, itemGlobalLinks from schema) - `export type ItemGlobalLink = typeof itemGlobalLinks.$inferSelect;` - `export type SearchGlobalItems = z.infer;` - `export type LinkItem = z.infer;` - `export type UpdateProfile = z.infer;` **global-items-seed.json** — Create per D-06, D-07. Array of 15-20 bikepacking gear items covering categories like bags (frame bags, handlebar bags, saddle bags), shelters (tents, bivvies, tarps), sleep systems (sleeping bags, pads), cooking, hydration, and lighting. Each object has: `brand`, `model`, `category`, `weightGrams`, `priceCents`, `description`. Use real product names and approximate specs (e.g., Revelate Designs Terrapin, Apidura Expedition Handlebar Pack, Sea to Summit Spark SP1, MSR PocketRocket 2, Nemo Tensor Ultralight). Do NOT include `id` or `createdAt`. bun run lint 2>&1 | tail -5 - grep -q "searchGlobalItemsSchema" src/shared/schemas.ts - grep -q "linkItemSchema" src/shared/schemas.ts - grep -q "updateProfileSchema" src/shared/schemas.ts - grep -q "GlobalItem" src/shared/types.ts - grep -q "UpdateProfile" src/shared/types.ts - test -f src/db/global-items-seed.json Zod schemas cover global item search, item linking, profile update, and setup visibility. Types inferred from schemas and Drizzle tables. Seed file has 15-20 bikepacking items with real product names. - `bun run lint` passes with no errors - `grep -c "export const" src/db/schema.ts` shows new table exports - `bun run db:generate` creates a clean migration - Seed JSON is valid: `node -e "JSON.parse(require('fs').readFileSync('src/db/global-items-seed.json','utf8'))"` Schema.ts has globalItems, itemGlobalLinks, user profile columns, and setup isPublic. Schemas.ts has all new Zod validators. Types.ts exports all new types. Seed JSON file exists with 15-20 items. Lint passes. After completion, create `.planning/phases/18-global-items-public-profiles/18-01-SUMMARY.md`