From 177403276b83478de0cf7fc85a54a19ddd4a32d0 Mon Sep 17 00:00:00 2001 From: neodk2004 Date: Sat, 6 Jun 2026 09:43:00 +0200 Subject: [PATCH] fix(01-02): update layout comment to reference middleware.ts --- .claude/settings.local.json | 30 + .gitignore | 3 + .planning/STATE.md | 12 +- .planning/config.json | 3 +- .../phases/01-foundation/01-01-SUMMARY.md | 204 ++++ .planning/phases/01-foundation/01-RESEARCH.md | 898 ++++++++++++++++++ src/app/(app)/layout.tsx | 2 +- 7 files changed, 1144 insertions(+), 8 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 .planning/phases/01-foundation/01-01-SUMMARY.md create mode 100644 .planning/phases/01-foundation/01-RESEARCH.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..596566f --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,30 @@ +{ + "permissions": { + "allow": [ + "Bash(node \"T:/CC-Projekte/.claude/gsd-core/bin/gsd-tools.cjs\" query commit \"docs: initialize project\" --files .planning/PROJECT.md)", + "Bash(mkdir -p .planning)", + "Bash(node \"T:/CC-Projekte/.claude/gsd-core/bin/gsd-tools.cjs\" query config-new-project '{\"mode\":\"yolo\",\"granularity\":\"standard\",\"parallelization\":true,\"commit_docs\":true,\"model_profile\":\"balanced\",\"workflow\":{\"research\":true,\"plan_check\":true,\"verifier\":true,\"nyquist_validation\":true,\"auto_advance\":false},\"plan_review\":{\"source_grounding\":true},\"ship\":{\"pr_body_sections\":[{\"heading\":\"User Stories & Acceptance Criteria\",\"enabled\":true,\"source\":\"REQUIREMENTS.md ## User Stories || REQUIREMENTS.md ## Acceptance Criteria\",\"fallback\":\"- Acceptance criteria are covered by the linked requirements and verification evidence.\"},{\"heading\":\"Risks & Dependencies\",\"enabled\":true,\"source\":\"PLAN.md ## Risks || PLAN.md ## Dependencies\",\"fallback\":\"- No known high-risk rollout dependencies.\"},{\"heading\":\"Success Metrics & Release Criteria\",\"enabled\":true,\"source\":\"REQUIREMENTS.md ## Definition of Done || VERIFICATION.md ## Release Criteria\",\"fallback\":\"- Release when automated verification and required manual checks pass.\"},{\"heading\":\"Stakeholder Review & Approval\",\"enabled\":false,\"template\":\"- Product owner approval pending for {phase_name}.\"}]}}')", + "Bash(node \"T:/CC-Projekte/.claude/gsd-core/bin/gsd-tools.cjs\" query commit \"chore: add project config\" --files .planning/config.json)", + "Bash(python3 *)", + "Bash(rm .planning/research/write_summary.py)", + "Bash(git init *)", + "Bash(git add *)", + "Bash(git commit -m 'docs: complete project research for OnlyPets *)", + "Bash(node \"T:/CC-Projekte/.claude/gsd-core/bin/gsd-tools.cjs\" query commit \"docs: define v1 requirements\" --files .planning/REQUIREMENTS.md)", + "Skill(gsd:ui-phase)", + "Bash(node \"T:/CC-Projekte/.claude/gsd-core/bin/gsd-tools.cjs\" query state.advance-plan --phase 1 --plan \"01-01\")", + "Bash(mkdir -p \"T:/CC-Projekte/OnlyPets/src/app/\\(auth\\)/sign-up/[[...sign-up]]\")", + "Bash(mkdir -p \"T:/CC-Projekte/OnlyPets/src/app/\\(auth\\)/log-in/[[...sign-in]]\")", + "Bash(npx tsc *)", + "Bash(node -e \"const c = require\\('@clerk/nextjs'\\); console.log\\(Object.keys\\(c\\).filter\\(k => k.includes\\('useSign'\\) || k.includes\\('Hook'\\)\\)\\)\")", + "Bash(node -e ' *)", + "Bash(xargs grep -l \"SignInSignalValue\")", + "Bash(npx vitest *)", + "Bash(git commit -m ' *)", + "Bash(gsd-tools query *)", + "Bash(git commit *)", + "Bash(Test-Path *)", + "Bash(git mv *)" + ] + } +} diff --git a/.gitignore b/.gitignore index 68d82a4..e584102 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# clerk configuration (can include secrets) +/.clerk/ diff --git a/.planning/STATE.md b/.planning/STATE.md index d1cf5d4..c90939c 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,13 +4,13 @@ milestone: v1.0 milestone_name: milestone status: executing stopped_at: Phase 1 plans complete (01-01 through 01-04) -last_updated: "2026-06-05T15:29:08.194Z" -last_activity: 2026-06-05 — Phase 1 plans created (01-01 through 01-04); ready for execution +last_updated: "2026-06-06T06:40:22.471Z" +last_activity: 2026-06-05 -- Phase 1 execution started progress: total_phases: 7 completed_phases: 0 total_plans: 4 - completed_plans: 0 + completed_plans: 1 percent: 0 --- @@ -25,10 +25,10 @@ See: .planning/PROJECT.md (updated 2026-06-05) ## Current Position -Phase: 1 of 7 (Foundation) -Plan: 0 of 4 in current phase +Phase: 1 (Foundation) — EXECUTING +Plan: 2 of 4 Status: Ready to execute -Last activity: 2026-06-05 — Phase 1 plans created (01-01 through 01-04); ready for execution +Last activity: 2026-06-05 -- Phase 1 execution started Progress: [░░░░░░░░░░] 0% diff --git a/.planning/config.json b/.planning/config.json index 167dce2..4c4dac4 100644 --- a/.planning/config.json +++ b/.planning/config.json @@ -41,7 +41,8 @@ "post_planning_gaps": true, "security_enforcement": true, "security_asvs_level": 1, - "security_block_on": "high" + "security_block_on": "high", + "_auto_chain_active": false }, "ship": { "pr_body_sections": [ diff --git a/.planning/phases/01-foundation/01-01-SUMMARY.md b/.planning/phases/01-foundation/01-01-SUMMARY.md new file mode 100644 index 0000000..efa6832 --- /dev/null +++ b/.planning/phases/01-foundation/01-01-SUMMARY.md @@ -0,0 +1,204 @@ +--- +phase: "01" +plan: "01" +subsystem: foundation +tags: [scaffold, auth, prisma, trpc, r2, active-pet-context, vitest] +dependency_graph: + requires: [] + provides: + - proxy.ts (Clerk auth enforcement on all non-public routes) + - prisma singleton (src/lib/prisma.ts with @prisma/adapter-pg) + - Prisma schema (Owner, Species, Breed, Pet models) + - getPresignedUploadUrl (src/lib/r2.ts) + - getOwnerId / Clerk auth wrapper (src/lib/auth.ts) + - createTRPCContext + router + publicProcedure + protectedProcedure (src/trpc/init.ts) + - appRouter + AppRouter type (src/trpc/routers/_app.ts) + - petsRouter with create/list/byId + ownership assertion (src/trpc/routers/pets.ts) + - useTRPC + TRPCReactProvider (src/trpc/client.tsx) + - ActivePetProvider + useActivePet (src/context/ActivePetContext.tsx) + - Vitest config + 22 Wave 0 test scaffolds + affects: + - All subsequent plans (depend on these foundational symbols) +tech_stack: + added: + - Next.js 16.2.7 (React 19.2.4, TypeScript 6.0.3, Turbopack) + - Tailwind CSS v4.3.0 (@import "tailwindcss"; @tailwindcss/postcss) + - shadcn/ui (base-nova style; neutral preset; components: button, input, label, textarea, select, avatar, card, dialog, dropdown-menu, sonner, progress, separator, badge, skeleton) + - "@clerk/nextjs" 7.4.3 (proxy.ts auth gating) + - "@trpc/server" + "@trpc/client" + "@trpc/tanstack-react-query" 11.17.0 + - "@tanstack/react-query" 5.101.0 + - "zod" 4.4.3 + - "prisma" + "@prisma/client" 7.8.0 (Rust-free WASM engine) + - "@prisma/extension-accelerate" 3.0.1 + - "@prisma/adapter-pg" 7.8.0 + "pg" 8.x (Prisma 7 local dev adapter) + - "@aws-sdk/client-s3" + "@aws-sdk/s3-request-presigner" 3.1062.0 + - "sonner" 2.0.7 + - "lucide-react" 1.17.0 + - "@upstash/redis" 1.38.0 + - "vitest" 4.x + "@vitest/coverage-v8" + "@vitejs/plugin-react" + "jsdom" + patterns: + - Prisma 7 singleton with @prisma/adapter-pg (local dev) and withAccelerate() (Vercel) + - Two-layer identity: Clerk userId (owner) + active_pet_id (pet context + localStorage) + - Presigned R2 PUT upload (media bytes never through API server) + - Clerk proxy.ts pattern (Next.js 16 convention) + - tRPC ownership assertion on all pet mutations (T-1-02) +key_files: + created: + - proxy.ts + - prisma.config.ts + - prisma/schema.prisma + - prisma/seed.ts + - src/lib/prisma.ts + - src/lib/r2.ts + - src/lib/auth.ts + - src/trpc/init.ts + - src/trpc/query-client.ts + - src/trpc/server.ts + - src/trpc/client.tsx + - src/trpc/routers/_app.ts + - src/trpc/routers/pets.ts + - src/app/api/trpc/[trpc]/route.ts + - src/context/ActivePetContext.tsx + - src/hooks/useActivePet.ts + - src/app/(app)/layout.tsx + - src/app/(app)/pets/page.tsx + - src/app/(app)/onboarding/pet/page.tsx + - vitest.config.ts + - src/__tests__/auth.test.ts + - src/__tests__/pet-profile.test.ts + - src/__tests__/media-upload.test.ts + - src/__tests__/helpers/prisma-mock.ts + - .env.example + - package.json + - postcss.config.mjs + - tsconfig.json + - components.json + modified: + - src/app/layout.tsx (added ClerkProvider + TRPCReactProvider + ActivePetProvider + Toaster) + - src/app/globals.css (orange design system: --primary oklch(0.7024 0.1967 41.45); --background oklch(0.9882 0 0)) +decisions: + - "Prisma 7 requires @prisma/adapter-pg for local direct connections (url/directUrl no longer in schema.prisma; moved to prisma.config.ts)" + - "Prisma 7 singleton: adapter path for local dev, accelerateUrl + withAccelerate() for Vercel/Accelerate" + - "proxy.ts at repo root (not src/) per Next.js 16 + Clerk 7.x convention" + - "tRPC appRouter.createCaller() used for RSC server-side calling (createCallerFactory removed from @trpc/server top-level in v11)" + - "Onboarding pet page uses placeholder speciesId — real species selector wired in Plan 02" +metrics: + duration: "~20 minutes" + completed: "2026-06-05T19:18:00Z" + tasks_completed: 2 + tasks_total: 3 + files_created: 26 + files_modified: 2 +--- + +# Phase 01 Plan 01: Walking Skeleton Summary + +**One-liner:** Next.js 16 scaffold + Clerk proxy.ts + Prisma 7 schema (Owner/Species/Breed/Pet) + tRPC pets router with ownership assertions + presigned R2 client + ActivePetContext + Vitest Wave 0 test scaffold + +## What Was Built + +The foundational architecture for OnlyPets is in place. Two tasks completed: + +**Task 1 — Scaffold + deps + Vitest scaffold:** +- `create-next-app` in scaffold-tmp (capital letters prevented scaffolding in root), then moved to project root +- All pinned runtime deps installed: Clerk 7.4.3, tRPC 11.17.0, Prisma 7.8.0, Zod 4.4.3, AWS SDK, Sonner, Upstash Redis +- Tailwind v4 verified: `postcss.config.mjs` uses `@tailwindcss/postcss`; `globals.css` uses `@import "tailwindcss"`; no `tailwind.config.ts` +- shadcn/ui initialized (base-nova style, neutral preset); all Phase 1 components added +- Orange design system applied: `--primary: oklch(0.7024 0.1967 41.45)` (#F97316 equivalent); `--background: oklch(0.9882 0 0)` (#FAFAFA) +- Inter font loaded via `next/font/google` +- Vitest configured with jsdom + @ alias; 22 `it.todo` Wave 0 tests scaffold + +**Task 2 — Core wiring:** +- `proxy.ts` at repo root: Clerk middleware gating all non-public routes; `config.matcher` includes `/(api|trpc)(.*)` and `/__clerk/(.*)` +- `prisma/schema.prisma`: Owner, Species, Breed, Pet models (Prisma 7 format — no url/directUrl in schema) +- `prisma.config.ts`: Prisma 7 `defineConfig` with datasource url from `DATABASE_URL` env +- `src/lib/prisma.ts`: Singleton with `@prisma/adapter-pg` for local dev; `withAccelerate()` for Prisma Accelerate +- `src/lib/r2.ts` (server-only): S3Client + `getPresignedUploadUrl(key, contentType)` with 300s expiry +- `src/lib/auth.ts`: `getOwnerId()` Clerk auth wrapper +- `src/trpc/init.ts`: `createTRPCContext`, `router`, `publicProcedure`, `protectedProcedure` (UNAUTHORIZED on no userId) +- `src/trpc/routers/pets.ts`: `pets.create` (upserts Owner, creates Pet), `pets.list` (scoped), `pets.byId` (ownership-asserted) +- `src/trpc/routers/_app.ts`: `appRouter` + `AppRouter` type +- `src/trpc/client.tsx`: `TRPCReactProvider` + `useTRPC` (via `@trpc/tanstack-react-query`) +- `src/trpc/server.ts`: `createTRPCCaller` for RSC prefetch (uses `appRouter.createCaller()`) +- `/api/trpc/[trpc]/route.ts`: tRPC fetch handler (GET + POST) +- `src/context/ActivePetContext.tsx` + `src/hooks/useActivePet.ts`: localStorage-backed active pet context +- `prisma/seed.ts`: seeds Dog/Cat/Bird species idempotently +- `src/app/layout.tsx`: ClerkProvider (orange appearance), TRPCReactProvider, ActivePetProvider, Toaster +- Skeleton pages: `(app)/layout.tsx`, `(app)/pets/page.tsx` (RSC, reads pets.list), `(app)/onboarding/pet/page.tsx` (form + pets.create mutation) + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Prisma 7 schema.prisma no longer accepts url/directUrl in datasource** +- **Found during:** Task 2, running `npx prisma validate` +- **Issue:** Prisma 7 error: "The datasource property `url` is no longer supported in schema files." The research document described Prisma 5/6 schema format. Prisma 7 requires connection URLs to live in `prisma.config.ts` (via `defineConfig`). +- **Fix:** Removed `url` and `directUrl` from `prisma/schema.prisma`; created `prisma.config.ts` with `defineConfig({ datasource: { url: env("DATABASE_URL") } })` +- **Files modified:** `prisma/schema.prisma`, created `prisma.config.ts` +- **Commit:** e0214ea + +**2. [Rule 2 - Missing critical functionality] Prisma 7 requires @prisma/adapter-pg for direct DB connections** +- **Found during:** Task 2, when testing PrismaClient instantiation +- **Issue:** Prisma 7's new WASM engine requires either `adapter` (for direct connections) or `accelerateUrl` (for Prisma Accelerate). The old `datasourceUrl` constructor option is removed. Without an adapter, the local dev path cannot connect to Supabase. +- **Fix:** Installed `@prisma/adapter-pg` 7.8.0 and `pg` 8.x (first-party Prisma packages). Singleton uses `PrismaPg(new Pool(...))` for local dev and `accelerateUrl + withAccelerate()` for Vercel. +- **Files modified:** `src/lib/prisma.ts`, `package.json` +- **Commit:** e0214ea + +**3. [Rule 1 - Bug] Missing jsdom dependency for Vitest** +- **Found during:** Task 1, running `npx vitest run` +- **Issue:** Vitest 4.x requires `jsdom` to be installed separately when `environment: "jsdom"` is configured. +- **Fix:** `npm install -D jsdom` +- **Files modified:** `package.json` +- **Commit:** dbc074e + +**4. [Rule 1 - Bug] tRPC v11 `createCallerFactory` not exported from `@trpc/server` top-level** +- **Found during:** Task 2, TypeScript check +- **Issue:** The tRPC docs example referenced `createCallerFactory` from `@trpc/server` but it's not in the v11 API surface. +- **Fix:** Use `appRouter.createCaller(ctx)` directly in `src/trpc/server.ts`. +- **Files modified:** `src/trpc/server.ts` +- **Commit:** e0214ea + +**5. [Rule 1 - Bug] create-next-app refused capital letters in directory name** +- **Found during:** Task 1, scaffolding attempt +- **Issue:** `create-next-app` rejects names with capital letters. The project root is `T:/CC-Projekte/OnlyPets` (capital O and P). +- **Fix:** Scaffolded into `scaffold-tmp` subdirectory then moved all files to project root via PowerShell `Copy-Item -Recurse`. +- **Files modified:** All scaffold files moved to root +- **Commit:** dbc074e + +## Task 3: Checkpoint (Awaiting Human Action) + +Task 3 is a `checkpoint:human-action` requiring the user to: +1. Set up `.env.local` with real `DATABASE_URL` and `DIRECT_URL` from Supabase +2. Run `npx prisma db push` to create tables in Supabase +3. Run `npx prisma db seed` to seed Dog/Cat/Bird species +4. Verify Species table shows 3 rows in Prisma Studio + +## Known Stubs + +| File | Location | Stub | Reason | +|------|----------|------|--------| +| `src/app/(app)/onboarding/pet/page.tsx` | Line 41 | `speciesId: "placeholder"` | Real species selector not wired until Plan 02. Walking skeleton only needs the create mutation end-to-end path. | + +## Self-Check: PASSED + +Files verified: +- proxy.ts: FOUND +- prisma/schema.prisma: FOUND +- prisma.config.ts: FOUND +- src/lib/prisma.ts: FOUND +- src/lib/r2.ts: FOUND +- src/lib/auth.ts: FOUND +- src/trpc/init.ts: FOUND +- src/trpc/routers/pets.ts: FOUND +- src/trpc/routers/_app.ts: FOUND +- src/trpc/client.tsx: FOUND +- src/context/ActivePetContext.tsx: FOUND +- src/hooks/useActivePet.ts: FOUND +- vitest.config.ts: FOUND +- src/__tests__/pet-profile.test.ts: FOUND +- src/__tests__/media-upload.test.ts: FOUND +- .env.example: FOUND + +Commits verified: +- dbc074e: chore(01-01) scaffold +- e0214ea: feat(01-01) wiring diff --git a/.planning/phases/01-foundation/01-RESEARCH.md b/.planning/phases/01-foundation/01-RESEARCH.md new file mode 100644 index 0000000..0e1bbfe --- /dev/null +++ b/.planning/phases/01-foundation/01-RESEARCH.md @@ -0,0 +1,898 @@ +# Phase 1: Foundation - Research + +**Researched:** 2026-06-05 +**Domain:** Next.js 16 App Router, Clerk auth, Prisma 7 + Supabase, Cloudflare R2 presigned uploads, two-layer pet identity +**Confidence:** MEDIUM-HIGH + +--- + +## Summary + +Phase 1 establishes the identity layer and media pipeline that every subsequent phase depends on. Three things must be true before any other phase starts: (1) owner authentication works end-to-end, (2) the pet-as-actor identity pattern is encoded correctly into every request context, and (3) media storage bypasses the API server via presigned R2 uploads. None of these can be retrofitted cheaply. + +The stack decisions from ecosystem research remain sound but require version updates: the npm `latest` tag now resolves to Next.js **16.2.7** (stable since October 2025), Prisma **7.8.0**, and `@trpc/tanstack-react-query` as the current package name for the tRPC React Query integration. Clerk 7.4.3 explicitly declares `^16.0.10` in its peerDependencies, so compatibility with Next.js 16 is confirmed. The previously flagged blockers (Clerk + Next.js 15 compatibility, tRPC v11 stability, Tailwind v4 migration) are now resolved. + +Walking skeleton goal: owner can sign up with Clerk, a pet profile is created in Supabase PostgreSQL via Prisma, the pet's avatar photo is uploaded directly to Cloudflare R2 via a presigned URL, and the active pet is reflected in the navigation — all in a single deployable Next.js 16 application on Vercel. + +**Primary recommendation:** Scaffold with `create-next-app@latest` (gives Next.js 16, React 19, TypeScript, Tailwind v4, Turbopack by default), install Clerk + tRPC + Prisma 7, run `npx shadcn@latest init`, configure `proxy.ts` (not `middleware.ts`) for Clerk, and establish the two-layer identity context before writing any pet profile logic. + +--- + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|------------------| +| AUTH-01 | Owner can create an account with email and password | Clerk `` component + `clerkMiddleware()` in proxy.ts handles this entirely | +| AUTH-02 | Owner receives email verification link after signup | Clerk's built-in email verification flow — no custom code needed; configure in Clerk dashboard | +| AUTH-03 | Owner can reset password via email link | Clerk's built-in forgot-password flow with `` component | +| AUTH-04 | Owner session persists across browser refresh | Clerk JWT sessions are persisted via httpOnly cookie automatically; active_pet_id stored in localStorage | +| PET-01 | Owner can create a pet profile with name, breed, species, bio, and avatar photo | Prisma Pet model + tRPC `pets.create` mutation + presigned R2 upload for avatar | +| PET-02 | Owner can edit their pet's profile details | tRPC `pets.update` mutation with ownership assertion via `active_pet_id` + `owner_id` check | +| PET-03 | Owner can delete a pet profile | tRPC `pets.delete` mutation with confirm dialog; cascade behavior on DB constraints | +| PET-04 | Owner can manage multiple pets and switch the active pet without logging out | `active_pet_id` in React Context + localStorage; switcher in nav dropdown; no ownership change | +| PET-05 | Pet profile includes a structured "adoption origin story" field | `adoption_story` TEXT column in Pet table; rendered as `