From fb76e7cd7bc9ad96f450cca213b6d2161ad8ca0b Mon Sep 17 00:00:00 2001 From: neodk2004 Date: Sun, 7 Jun 2026 12:28:42 +0200 Subject: [PATCH] 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) --- .../phases/02-content-core/02-01-SUMMARY.md | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 .planning/phases/02-content-core/02-01-SUMMARY.md diff --git a/.planning/phases/02-content-core/02-01-SUMMARY.md b/.planning/phases/02-content-core/02-01-SUMMARY.md new file mode 100644 index 0000000..9bc889d --- /dev/null +++ b/.planning/phases/02-content-core/02-01-SUMMARY.md @@ -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