191 lines
7.8 KiB
Markdown
191 lines
7.8 KiB
Markdown
---
|
|
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>
|