---
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"
---

<objective>
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
</objective>

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

<context>
@.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
</context>

<tasks>

<task type="auto">
  <name>Task 1: Schema tables and column additions</name>
  <files>src/db/schema.ts</files>
  <read_first>src/db/schema.ts</read_first>
  <action>
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.
  </action>
  <verify>
    <automated>bun run db:generate 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - 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
  </acceptance_criteria>
  <done>All four schema additions (globalItems table, itemGlobalLinks table, users profile columns, setups isPublic) are defined and exported. Migration generated successfully.</done>
</task>

<task type="auto">
  <name>Task 2: Zod schemas, types, and seed data</name>
  <files>src/shared/schemas.ts, src/shared/types.ts, src/db/global-items-seed.json</files>
  <read_first>src/shared/schemas.ts, src/shared/types.ts</read_first>
  <action>
**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<typeof searchGlobalItemsSchema>;`
- `export type LinkItem = z.infer<typeof linkItemSchema>;`
- `export type UpdateProfile = z.infer<typeof updateProfileSchema>;`

**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`.
  </action>
  <verify>
    <automated>bun run lint 2>&1 | tail -5</automated>
  </verify>
  <acceptance_criteria>
    - 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
  </acceptance_criteria>
  <done>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.</done>
</task>

</tasks>

<verification>
- `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'))"`
</verification>

<success_criteria>
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.
</success_criteria>

<output>
After completion, create `.planning/phases/18-global-items-public-profiles/18-01-SUMMARY.md`
</output>
