docs(02): capture phase context
This commit is contained in:
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.
|
||||
Reference in new issue
Block a user