docs(02-01): complete foundation scaffold plan summary
- SUMMARY.md for Phase 02 Plan 01 (Foundation Scaffold, Wave 0) - 8 Prisma models pushed, 7 router stubs registered, 7 test scaffolds created - 2 auto-fixed deviations documented (ZRANGE offset, vi import)
This commit is contained in:
1 parent
4c8ca3ab2f
commit
fb76e7cd7b
1 file changed
+202
@@ -0,0 +1,202 @@
|
||||
---
|
||||
phase: "02"
|
||||
plan: "01"
|
||||
subsystem: content-foundation
|
||||
tags: [prisma, schema, trpc, redis, feed, social-graph, vitest, shadcn]
|
||||
dependency_graph:
|
||||
requires:
|
||||
- Phase 01 Plan 01 (proxy.ts, prisma singleton, tRPC init, petsRouter)
|
||||
provides:
|
||||
- 8 Prisma models + 3 enums in DB (Post, PostImage, Milestone, Story, StoryView, Follow, Block, Report + PostType/MilestoneType/ReportReason)
|
||||
- Pet back-relations for all Phase 2 social graph (posts, stories, storyViews, following, followers, blocking, blockedBy, reports)
|
||||
- src/lib/redis.ts — @upstash/redis singleton (server-only)
|
||||
- src/lib/feed-helpers.ts — feedKey, fanOutPost, backfillOnFollow, getFeedPage, trimFeed, FEED_RETENTION_MS
|
||||
- src/lib/media-url.ts — getMediaUrl (client-safe, mirrors getAvatarUrl)
|
||||
- src/lib/milestone-thresholds.ts — FOLLOWER_MILESTONE_THRESHOLDS constant (D-09)
|
||||
- 7 tRPC router stubs registered in _app.ts (posts, stories, milestones, follows, blocks, reports, feed)
|
||||
- 7 test scaffold files (21 it.todo stubs covering all Phase 2 requirements)
|
||||
- react-intersection-observer ^10.0.3
|
||||
- 7 shadcn components (carousel, sheet, alert-dialog, scroll-area, tabs, popover, radio-group)
|
||||
affects:
|
||||
- All Phase 2 Wave 1+ plans (build implementations against these contracts)
|
||||
tech_stack:
|
||||
added:
|
||||
- react-intersection-observer 10.0.3 (feed scroll sentinel)
|
||||
- shadcn carousel (wraps Embla) + sheet + alert-dialog + scroll-area + tabs + popover + radio-group
|
||||
- 8 new Prisma models pushed to database
|
||||
patterns:
|
||||
- Redis sorted-set fan-out feed (feedKey, fanOutPost, ZADD pipeline)
|
||||
- Feed cursor pagination (@upstash/redis ZRANGE byScore+rev+withScores, offset+count)
|
||||
- FEED_RETENTION_MS = 90-day trim via trimFeed (called by cleanup job)
|
||||
- server-only import on all server libs (T-02-02)
|
||||
- Explicit named Prisma self-relations (Follower/Followee, Blocker/Blocked, StoryViewer, Reporter)
|
||||
- StoryView NO onDelete cascade (D-08 — views survive story expiry)
|
||||
- MilestoneType hard-coded constant array (A3, D-09)
|
||||
- posts/ storage key prefix for path-traversal prevention (T-02)
|
||||
key_files:
|
||||
created:
|
||||
- prisma/schema.prisma (modified — Phase 2 models appended)
|
||||
- src/lib/redis.ts
|
||||
- src/lib/feed-helpers.ts
|
||||
- src/lib/media-url.ts
|
||||
- src/lib/milestone-thresholds.ts
|
||||
- src/trpc/routers/posts.ts
|
||||
- src/trpc/routers/stories.ts
|
||||
- src/trpc/routers/milestones.ts
|
||||
- src/trpc/routers/follows.ts
|
||||
- src/trpc/routers/blocks.ts
|
||||
- src/trpc/routers/reports.ts
|
||||
- src/trpc/routers/feed.ts
|
||||
- src/__tests__/posts.test.ts
|
||||
- src/__tests__/stories.test.ts
|
||||
- src/__tests__/milestones.test.ts
|
||||
- src/__tests__/follows.test.ts
|
||||
- src/__tests__/feed.test.ts
|
||||
- src/__tests__/blocks.test.ts
|
||||
- src/__tests__/reports.test.ts
|
||||
- src/components/ui/carousel.tsx
|
||||
- src/components/ui/sheet.tsx
|
||||
- src/components/ui/alert-dialog.tsx
|
||||
- src/components/ui/scroll-area.tsx
|
||||
- src/components/ui/tabs.tsx
|
||||
- src/components/ui/popover.tsx
|
||||
- src/components/ui/radio-group.tsx
|
||||
modified:
|
||||
- prisma/schema.prisma (Phase 2 enums + models + Pet back-relations appended)
|
||||
- src/trpc/routers/_app.ts (7 new routers registered)
|
||||
- src/__tests__/helpers/prisma-mock.ts (8 new model mocks added)
|
||||
decisions:
|
||||
- "@upstash/redis zrange requires {offset, count} together — count alone is not valid (Rule 1 fix applied)"
|
||||
- "Post images use posts/ storage key prefix in same pet-avatars bucket (RESEARCH A1)"
|
||||
- "Stories use stories/ storage key prefix (consistent with posts/ convention)"
|
||||
- "Feed sorted set: own posts included in poster's own feed inbox (Open Question 3 recommendation)"
|
||||
- "FEED_RETENTION_MS = 90 days; trimFeed uses ZREMRANGEBYSCORE (Open Question 1 recommendation)"
|
||||
- "Vitest globals: vi must be explicitly imported even with globals:true for TypeScript (Rule 1 fix applied)"
|
||||
metrics:
|
||||
duration: "~35 minutes"
|
||||
completed: "2026-06-07T12:27:00Z"
|
||||
tasks_completed: 3
|
||||
tasks_total: 3
|
||||
files_created: 25
|
||||
files_modified: 3
|
||||
---
|
||||
|
||||
# Phase 02 Plan 01: Foundation Scaffold Summary
|
||||
|
||||
**One-liner:** 8 Prisma models pushed to DB + @upstash/redis feed helpers + 7 tRPC router stubs registered + 7 test scaffolds + react-intersection-observer + 7 shadcn components installed
|
||||
|
||||
## What Was Built
|
||||
|
||||
The complete data and contract foundation for all Phase 2 Wave 1+ plans.
|
||||
|
||||
**Task 1 — Prisma schema: 8 models, 3 enums, Pet back-relations, db push:**
|
||||
- Appended `PostType`, `MilestoneType` (16 values: 5 user-initiated + 11 follower thresholds per D-09), and `ReportReason` enums to `prisma/schema.prisma`
|
||||
- Added 8 models: `Post`, `PostImage`, `Milestone`, `Story`, `StoryView`, `Follow`, `Block`, `Report`
|
||||
- Added Pet back-relations for all social graph relations
|
||||
- Explicit named relations on all self-referential models to prevent "ambiguous relation" errors (Pitfall 3)
|
||||
- `StoryView.storyId` relation has NO `onDelete: Cascade` (D-08 — views survive story expiry)
|
||||
- `npx prisma validate` + `npx prisma generate` + `npx prisma db push` all succeeded
|
||||
|
||||
**Task 2 — Shared libs + router stubs + _app.ts registration:**
|
||||
- `src/lib/redis.ts`: `@upstash/redis` singleton with `import "server-only"` (T-02-02)
|
||||
- `src/lib/feed-helpers.ts`: `feedKey`, `fanOutPost`, `backfillOnFollow`, `getFeedPage`, `trimFeed`, `FEED_RETENTION_MS` — full fan-out pattern with ZRANGE `byScore+rev+withScores+offset+count`
|
||||
- `src/lib/media-url.ts`: `getMediaUrl` — client-safe, mirrors `getAvatarUrl` signature
|
||||
- `src/lib/milestone-thresholds.ts`: `FOLLOWER_MILESTONE_THRESHOLDS` constant (D-09, A3)
|
||||
- 7 tRPC router stubs (`posts`, `stories`, `milestones`, `follows`, `blocks`, `reports`, `feed`) — all `import "server-only"`, all procedures returning `NOT_IMPLEMENTED`
|
||||
- `feed.getFeed` input schema includes `cursor: z.number().nullish()` exactly per Pitfall 6
|
||||
- `_app.ts` updated to register all 9 routers (pets, media + 7 new)
|
||||
- `npx tsc --noEmit` exits 0
|
||||
|
||||
**Task 3 — Test scaffolds + deps + shadcn:**
|
||||
- Extended `src/__tests__/helpers/prisma-mock.ts` with mock methods for all 8 new models
|
||||
- Created 7 test scaffold files with 21 `it.todo` stubs covering every Phase 2 requirement
|
||||
- `feed.test.ts` mocks `@/lib/redis` — no real Upstash calls in tests
|
||||
- Installed `react-intersection-observer` ^10.0.3 (slopcheck [OK])
|
||||
- Added 7 shadcn components: carousel, sheet, alert-dialog, scroll-area, tabs, popover, radio-group
|
||||
- All 7 test files run with 21 todo stubs and 0 failures
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] @upstash/redis ZRANGE requires `offset` when `count` is provided**
|
||||
- **Found during:** Task 2, running `npx tsc --noEmit`
|
||||
- **Issue:** `redis.zrange(key, max, min, { byScore, rev, withScores, count })` TypeScript error: `offset` is required alongside `count` in the @upstash/redis 1.38.0 API signature. RESEARCH Pattern 2 code example omitted `offset`.
|
||||
- **Fix:** Added `offset: 0` to the ZRANGE options in `getFeedPage`
|
||||
- **Files modified:** `src/lib/feed-helpers.ts`
|
||||
- **Commit:** c435404
|
||||
|
||||
**2. [Rule 1 - Bug] `vi` not recognized as global in TypeScript despite vitest `globals: true`**
|
||||
- **Found during:** Task 3, running `npx tsc --noEmit`
|
||||
- **Issue:** vitest's `globals: true` enables runtime globals but TypeScript's type checking still requires `vi` to be explicitly imported. All 7 new test scaffold files used `vi.mock()` without importing `vi`.
|
||||
- **Fix:** Added `import { ..., vi } from "vitest"` to all 7 test scaffold files
|
||||
- **Files modified:** 7 test scaffold files
|
||||
- **Commit:** 4c8ca3a
|
||||
|
||||
### Pre-existing Issues (Out of Scope)
|
||||
|
||||
**1. `src/__tests__/media-upload.test.ts` fails with "supabaseUrl is required"**
|
||||
- **Status:** Pre-existing failure — confirmed by reverting to pre-task state; the test was already failing before Phase 2 work
|
||||
- **Root cause:** `supabase-storage.ts` calls `createClient()` at module load time. When `media.ts` is imported in the test, `createClient` runs before `vi.mock("@/lib/supabase-storage")` can intercept it
|
||||
- **Impact:** Does not affect Phase 2 requirements — the 7 new test scaffold files all pass
|
||||
- **Logged to:** This SUMMARY (pre-existing)
|
||||
|
||||
## Known Stubs
|
||||
|
||||
| File | Stub | Reason |
|
||||
|------|------|--------|
|
||||
| `src/trpc/routers/posts.ts` | All 6 procedures throw `NOT_IMPLEMENTED` | Implementation in Wave 1 plans (02-02+) |
|
||||
| `src/trpc/routers/stories.ts` | All 5 procedures throw `NOT_IMPLEMENTED` | Implementation in Wave 1 plans |
|
||||
| `src/trpc/routers/milestones.ts` | All 2 procedures throw `NOT_IMPLEMENTED` | Implementation in Wave 1 plans |
|
||||
| `src/trpc/routers/follows.ts` | All 5 procedures throw `NOT_IMPLEMENTED` | Implementation in Wave 1 plans |
|
||||
| `src/trpc/routers/blocks.ts` | All 2 procedures throw `NOT_IMPLEMENTED` | Implementation in Wave 1 plans |
|
||||
| `src/trpc/routers/reports.ts` | `create` throws `NOT_IMPLEMENTED` | Implementation in Wave 1 plans |
|
||||
| `src/trpc/routers/feed.ts` | `getFeed` throws `NOT_IMPLEMENTED` | Implementation in Wave 1 plans |
|
||||
| `src/__tests__/*.test.ts` | 21 `it.todo` stubs | Wave 0 scaffold — implementation in Wave 1 |
|
||||
|
||||
These stubs are intentional — this is the interface-first Wave 0 scaffold. Downstream plans implement against these contracts.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
No new security-relevant surface not in the plan's threat model. All new surface was covered:
|
||||
- T-02-01: Explicit named self-relations — implemented
|
||||
- T-02-02: `import "server-only"` on redis.ts and feed-helpers.ts — implemented
|
||||
- T-02-SC: Package legitimacy audit passed for react-intersection-observer [OK]
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
Files verified:
|
||||
- prisma/schema.prisma: FOUND
|
||||
- src/lib/redis.ts: FOUND
|
||||
- src/lib/feed-helpers.ts: FOUND
|
||||
- src/lib/media-url.ts: FOUND
|
||||
- src/lib/milestone-thresholds.ts: FOUND
|
||||
- src/trpc/routers/posts.ts: FOUND
|
||||
- src/trpc/routers/stories.ts: FOUND
|
||||
- src/trpc/routers/milestones.ts: FOUND
|
||||
- src/trpc/routers/follows.ts: FOUND
|
||||
- src/trpc/routers/blocks.ts: FOUND
|
||||
- src/trpc/routers/reports.ts: FOUND
|
||||
- src/trpc/routers/feed.ts: FOUND
|
||||
- src/trpc/routers/_app.ts: FOUND
|
||||
- src/__tests__/posts.test.ts: FOUND
|
||||
- src/__tests__/stories.test.ts: FOUND
|
||||
- src/__tests__/milestones.test.ts: FOUND
|
||||
- src/__tests__/follows.test.ts: FOUND
|
||||
- src/__tests__/feed.test.ts: FOUND
|
||||
- src/__tests__/blocks.test.ts: FOUND
|
||||
- src/__tests__/reports.test.ts: FOUND
|
||||
- src/__tests__/helpers/prisma-mock.ts: FOUND
|
||||
- src/components/ui/carousel.tsx: FOUND
|
||||
- src/components/ui/sheet.tsx: FOUND
|
||||
- src/components/ui/alert-dialog.tsx: FOUND
|
||||
- src/components/ui/scroll-area.tsx: FOUND
|
||||
- src/components/ui/tabs.tsx: FOUND
|
||||
- src/components/ui/popover.tsx: FOUND
|
||||
- src/components/ui/radio-group.tsx: FOUND
|
||||
|
||||
Commits verified:
|
||||
- b2db631: feat(02-01) Prisma schema
|
||||
- c435404: feat(02-01) shared libs + router stubs
|
||||
- c74fc0d: feat(02-01) test scaffolds + deps + shadcn
|
||||
- 4c8ca3a: fix(02-01) vi import TypeScript fix
|
||||
Reference in new issue
Block a user