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:
admin committed 2026-06-07 12:28:42 +02:00
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