docs(02): capture phase context

This commit is contained in:
admin committed 2026-06-06 12:17:45 +02:00
1 parent ce25ca088c
commit 6a5ecc3ebf
2 files changed
+247

No files matched your search

@@ -0,0 +1,122 @@
# Phase 2: Content Core - Context
**Gathered:** 2026-06-06
**Status:** Ready for planning
<domain>
## Phase Boundary
Phase 2 delivers the complete content layer for PawFeed: pets can post photos (single + carousel), stories (24h disappearing), and milestone posts (structured types). Pets follow each other, owners see a chronological home feed. Block and report are live before any content is publicly accessible. The home route becomes the feed.
</domain>
<decisions>
## Implementation Decisions
### Post Creation Flow
- **D-01:** Post creation entry point is the center **+** button in the mobile bottom nav and a corresponding button in the desktop sidebar. Tapping/clicking it opens a **bottom sheet** (Sheet component) with three type choices: Photo, Story, Milestone.
- **D-02:** After selecting a type from the sheet, the creation form opens as a **full-screen modal overlay** (Dialog). No URL navigation change — the owner stays on the current page while creating.
- **D-03:** After successfully posting, the modal closes and the owner **lands on the feed (/feed) with the new post at the top**. Feed re-fetches on mutation success.
### Home & Navigation
- **D-04:** After Phase 2, the **home route (/)** redirects to **/feed**. The feed is the primary landing page after login, replacing the current /pets redirect.
- **D-05:** The **"Home" link** in the sidebar and mobile bottom nav points to **/feed**. Label stays "Home" (universal social app convention).
- **D-06:** Onboarding redirect logic in the app layout stays: if user has zero pets → /onboarding/pet. If user has pets but no follows → show the feed with an empty state ("Follow some pets to see their posts here" + "Find Pets" CTA).
### Story Expiry
- **D-07:** Stories use **lazy-delete on read**: the `stories` table has an `expires_at` column. All story queries filter `WHERE expires_at > NOW()`. A weekly cleanup job (Supabase `pg_cron` or manual) purges old records. No real-time cron infrastructure needed in Phase 2.
- **D-08:** Story **view analytics are retained for 30 days** even after the story expires. When a story expires, its record stays in the DB with `expired: true` but view records (`story_views` table) are NOT cascade-deleted. A separate cleanup job purges `story_views` older than 30 days. This allows the owner to see historical engagement.
### Milestone Types
- **D-09:** Predefined milestone types for Phase 2 (no custom types — consistent visual format):
- `birthday` — "🎂 Birthday"
- `adoption_day` — "🐾 Adoption Day"
- `first_outing` — "🌿 First Outing"
- `vet_visit` — "⚕️ Vet Visit"
- `litter` — "🍼 New Litter" (female pets who have delivered a litter; free-text field for litter details)
- Follower milestones (auto-triggered, not user-initiated): `followers_500`, `followers_1k`, `followers_2_5k`, `followers_5k`, `followers_10k`, `followers_25k`, `followers_50k`, `followers_100k`, `followers_250k`, `followers_500k`, `followers_1m`
- **D-10:** **Follower milestones are auto-triggered**: when a pet's follower count crosses a threshold, the system sends the owner an in-app notification ("Max just reached 1,000 followers! 🎉") with a prompt to share the milestone post. The owner chooses whether to post. The milestone post itself is still user-initiated (from the notification or the + button).
- **D-11:** Custom milestone types are **deferred to a later phase**. Predefined list only for now.
### Claude's Discretion
- Feed pagination cursor implementation details (cursor vs offset, page size)
- Redis sorted set key naming convention for the fan-out feed
- Exact DB schema for `posts`, `stories`, `milestones` tables (beyond the decisions above)
- Story view deduplication logic (track unique viewers, not view count)
- Follow notification batching implementation
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### Design Contract
- `.planning/phases/02-content-core/02-UI-SPEC.md` — Phase 2 UI design contract: PostCard, StoryRing, MilestoneCard visual specs, all copy strings, spacing exceptions, new shadcn components required
### Phase 1 Foundation
- `.planning/phases/01-foundation/01-01-SUMMARY.md` — tRPC router patterns, Prisma schema, ActivePetContext
- `.planning/phases/01-foundation/01-04-SUMMARY.md` — Supabase Storage media pipeline (presigned upload → confirmUpload) — MUST reuse for photo posts
- `src/trpc/routers/pets.ts` — ownership assertion pattern (assertPetOwnership) — replicate for all new content mutations
- `src/trpc/routers/media.ts` — presigned upload flow — extend for photo post uploads
- `src/lib/supabase-storage.ts` — `createSignedUploadUrl`, `getPublicUrl` — reuse directly
- `src/lib/avatar-url.ts` — CDN URL construction pattern — replicate for post images
### Requirements
- `.planning/REQUIREMENTS.md` — Phase 2 requirements: PHOTO-01–04, STORY-01–03, MILE-01–02, SOCL-01–03, FEED-01, SAFE-01–02
### Research
- `.planning/research/SUMMARY.md` — Redis fan-out feed architecture, Supabase Realtime, story data model guidance
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- `src/trpc/routers/media.ts` + `src/lib/supabase-storage.ts`: Photo post uploads reuse the exact same presigned upload pipeline as avatar uploads. The tRPC `media.getPresignedUrl` + `media.confirmUpload` pattern is the blueprint.
- `src/components/pet/AvatarUpload.tsx`: The five-state upload component (idle/dragging/uploading/complete/error) is the reference implementation for any upload UI in Phase 2.
- `src/context/ActivePetContext.tsx`: All content is posted as the active pet. `useActivePet()` provides the `activePetId` for every mutation.
- `src/trpc/init.ts`: `protectedProcedure` + `assertPetOwnership` pattern — replicate for every content mutation.
- shadcn components already installed: Card, Dialog, Avatar, Badge, Skeleton, Separator, Button, Sheet (new in Phase 2)
### Established Patterns
- tRPC v11 mutation: `useMutation({ ...trpc.x.y.mutationOptions(), onSuccess/onError })`
- tRPC v11 query: `useQuery({ ...trpc.x.y.queryOptions(), staleTime })`
- Server component data fetching: `createTRPCCaller()` from `src/trpc/server.ts`
- Ownership scope: always scoped by `ctx.userId` from Clerk, never trust client-supplied owner IDs
- Supabase Storage key format: `{type}/{ownerId}/{petId}/{uuid}.{ext}` — use `posts/{ownerId}/{petId}/{uuid}.jpg` for post images
- CDN URL: `getAvatarUrl(key)` → replicate as `getMediaUrl(key)` for post images
### Integration Points
- New `posts` router and `stories` router slot into `src/trpc/routers/_app.ts`
- New Prisma models (`Post`, `Story`, `MilestonePost`, `Follow`, `Block`, `Report`) extend `prisma/schema.prisma`
- `src/app/(app)/layout.tsx`: add `/feed` to the protected route group; update home redirect logic per D-04
- `src/components/layout/Sidebar.tsx` + `MobileNav.tsx`: add + button and Home → /feed update per D-05
</code_context>
<specifics>
## Specific Ideas
- Story view analytics retained 30 days (see D-08) — `story_views` table with `viewed_at` timestamp, not cascade-deleted when story expires
- Follower milestone thresholds: 500, 1k, 2.5k, 5k, 10k, 25k, 50k, 100k, 250k, 500k, 1M — seed into a `milestone_thresholds` table or hard-code as a constant array
- Litter milestone: female pets only, free-text field for "how many puppies/kittens/chicks"
- Feed on empty state: "Follow some pets to see their posts here." + "Find Pets" CTA linking to /explore (Phase 3 feature — CTA can be a placeholder button in Phase 2)
</specifics>
<deferred>
## Deferred Ideas
- **Native advertising / sponsored posts** — Owner requested native ad blocks in the feed (styled like posts but labeled "Sponsored"). This is a monetization capability that requires its own phase (advertiser accounts, ad targeting, billing, content policy). Deferred to a future Monetization phase (Phase 8+).
- **Custom milestone types** (free-text) — Deferred to post-MVP. Predefined list only in Phase 2.
- **Auto-follower milestone notifications** infrastructure — The notification trigger (when follower count crosses threshold) depends on Phase 4 (Notifications). In Phase 2, we create the milestone post type and seed the thresholds, but the auto-trigger fires in Phase 4.
</deferred>
---
*Phase: 02-content-core*
*Context gathered: 2026-06-06*
@@ -0,0 +1,125 @@
# Phase 2: Content Core - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-06-06
**Phase:** 02-content-core
**Areas discussed:** Post creation flow, Home & navigation, Story expiry mechanism, Milestone type list
---
## Post Creation Flow
| Option | Description | Selected |
|--------|-------------|----------|
| Center + button → bottom sheet | Mobile: + in bottom nav opens choice sheet. Desktop: sidebar button. Instagram-style. | ✓ |
| Dedicated page /posts/new | Clicking + navigates to a full page with type selector. Cleaner URL. | |
| Inline in feed | Composer at top of feed like Facebook/Twitter. | |
**User's choice:** Center + button → bottom sheet
---
| Option | Description | Selected |
|--------|-------------|----------|
| Full-screen modal overlay | Opens dialog on top of current page. No URL change. | ✓ |
| Navigate to /posts/new?type=photo | URL changes, browser back works. | |
**User's choice:** Full-screen modal overlay
---
| Option | Description | Selected |
|--------|-------------|----------|
| Back to feed, new post at top | Modal closes, feed refreshes with new post. | ✓ |
| Navigate to pet profile grid | Go to /pets/[petId]. | |
| Stay on current page | Modal closes, success toast only. | |
**User's choice:** Back to feed, new post at top
---
## Home & Navigation
| Option | Description | Selected |
|--------|-------------|----------|
| Feed (/feed) — redirect / → /feed | Landing after login = content feed. | ✓ |
| Keep /pets as home | My Pets grid stays first screen. | |
| Context-dependent | No follows → /pets, has follows → /feed | |
**User's choice:** Feed (/feed) — Home nav link points to /feed
---
| Option | Description | Selected |
|--------|-------------|----------|
| Home → /feed | Home icon takes you to the feed. | ✓ |
| Rename to 'Feed' and link to /feed | More explicit label. | |
**User's choice:** Home → /feed (label stays "Home")
---
## Story Expiry Mechanism
| Option | Description | Selected |
|--------|-------------|----------|
| Lazy-delete on read | Filter expired stories in queries, weekly cleanup job. No extra infrastructure. | ✓ |
| Vercel Cron Job | Hourly cleanup endpoint. Requires Vercel Pro. | |
| Supabase Edge Functions + pg_cron | Scheduled Supabase function. Free but adds complexity. | |
**User's choice:** Lazy-delete on read
---
| Option | Description | Selected |
|--------|-------------|----------|
| Delete views with story | Cascade delete views when story purged. Simple, no GDPR complexity. | |
| Keep view analytics for 30 days | Historical engagement data retained. Separate cleanup job. | ✓ |
**User's choice:** Keep view analytics for 30 days — `story_views` table not cascade-deleted on story expiry.
---
## Milestone Type List
**User's choice (multiSelect):** Birthday, Adoption Day, First Outing, Vet Visit
**Additional types specified by user:**
- Litter milestone (female animals who have delivered a litter)
- Follower milestones: 500, 1k, 2.5k, 5k, 10k, 25k, 50k, 100k, 250k, 500k, 1M — auto-triggered when follower count crosses threshold
---
| Option | Description | Selected |
|--------|-------------|----------|
| Auto-triggered | System sends notification at threshold, owner decides to post. | ✓ |
| Manually posted only | Owner creates milestone post themselves. | |
**User's choice:** Auto-triggered follower milestones
---
| Option | Description | Selected |
|--------|-------------|----------|
| Yes, free-text custom type | Owner can type any milestone. | |
| Predefined list only | Consistent visual format. Custom types deferred. | ✓ |
**User's choice:** Predefined list only for Phase 2
---
## Claude's Discretion
- Feed pagination cursor implementation details
- Redis sorted set key naming convention
- Exact DB schema column names (beyond the structural decisions)
- Story view deduplication logic
- Follow notification batching
## Deferred Ideas
- **Native advertising / sponsored posts** — Feed with native ad blocks styled like posts but labeled "Sponsored". Monetization phase (Phase 8+).
- **Custom milestone types** — Free-text custom types. Post-MVP.
- **Auto-follower milestone notification trigger** — Depends on Phase 4 Notifications infrastructure.