Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
| # | Feature | Context | Priority | Status | Dependencies | Complexity |
|---|---------|---------|----------|--------|-------------|------------|
| 001 | Monorepo Scaffold — Backend and Frontend Packages | Platform | P0 | ✅ SHIPPED | None | M |
| 002 | Authentication and User Management | Account | P0 | 🔲 | feat-001 | M |
| 002 | Authentication and User Management | Account | P0 | ✅ SHIPPED | feat-001 | M |
| 003 | Design System Foundation — Tokens and Primitives | Frontend | P0 | 🔲 | feat-001 | M |
| 004 | Application Shell and Navigation | Frontend / Account | P0 | 🔲 | feat-001, feat-002, feat-003 | S |
| 005 | Campaign and Donor Database Schema | Campaign / Donor / Payments | P0 | 🔲 | feat-001 | M |
Expand Down
8 changes: 8 additions & 0 deletions .claude/context/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@
## Frontend

- **Vite version conflict in npm workspaces:** If the root `node_modules` has a different vite version than a package's `node_modules` (e.g., vite 7 at root from hoisting, vite 6 in frontend), TypeScript will report type mismatches for `@tailwindcss/vite` and `@vitejs/plugin-react` plugins in `vite.config.ts`. Fix by aligning the frontend's `vite` version to match the hoisted root version (e.g., `"vite": "^7.0.0"`).
- **`@clerk/react` v5.54.0 is broken:** The only `@clerk/react` v5 release is v5.54.0, which references `loadClerkUiScript` from `@clerk/shared` that doesn't exist in any v3.x. Use `@clerk/react@^6.0.0` instead. npm will install a nested `@clerk/react/node_modules/@clerk/shared@^4.0.0` to satisfy the dependency.
- **Vitest OOM when loading react-router or @clerk/react in isolation:** Running a test file in its own vitest worker that imports `react-router` or the real `@clerk/react` module graph causes JS heap OOM at 4GB in this environment. Fix: (1) use `pool: 'threads'` with `singleThread: true, isolate: false` in vitest config so all tests share one worker/module cache, and (2) in individual test files, mock heavy deps (`react-router`, `@clerk/react`) in the `vi.mock` factory to prevent loading the real modules. Also add explicit `afterEach(cleanup)` when using `isolate: false`.
- **`vite-env.d.ts` is required:** The frontend `tsconfig.app.json` does not include `vite/client` types by default. Without `src/vite-env.d.ts` containing `/// <reference types="vite/client" />`, TypeScript will report `Property 'env' does not exist on type 'ImportMeta'` for `import.meta.env` usage.
- **`!important` in CSS triggers Biome warnings:** The `noImportantStyles` rule from Biome's recommended ruleset flags all `!important` usage. The `prefers-reduced-motion` safety net overrides in `tokens.css` are intentional and produce warnings (not errors). These are acceptable — do not remove them.

- **Express 5 `req.params` types:** In Express 5 with `@types/express` v5, `req.params[key]` is typed as `string | string[] | undefined`. When you need a guaranteed string, cast: `req.params.id as string`. Avoid bracket notation like `req.params['id']` — Biome flags it with `useLiteralKeys`.
- **Biome `noUselessConstructor`:** A constructor that only calls `super(message)` with an identical signature to the parent is flagged as useless and should be removed. TypeScript will inherit the parent constructor automatically. This applies to domain error subclasses that pass a fixed message to `DomainError`.
- **`UserValidationError` has no constructor:** Unlike errors with fixed messages, `UserValidationError` accepts a dynamic message. After removing the useless constructor, callers pass the message directly to the inherited `DomainError(message)` constructor — TypeScript allows this because the parent constructor is public.
- **MockAuthAdapter `getAuthContext` never returns null:** The mock adapter's `getAuthContext` always returns an `AuthContext` (not `null`). Test code that relies on getting a 401 from lack of auth should use real Clerk adapter or test the router's own null-guard code path directly.

## Infrastructure

<!-- Add gotchas here as they are discovered, e.g.:
Expand Down
22 changes: 16 additions & 6 deletions .claude/context/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,28 @@

## API Patterns

<!-- Add patterns here as they are established, e.g.:
- Route wiring and DI conventions
- Error response format
- Zod validation middleware
-->
- **Router factory with DI:** Routers are created via `createXxxRouter(deps)` factory functions that accept injected dependencies (authAdapter, repos, services). This enables supertest testing without touching real infrastructure.
- **Error response format:** `{ error: { code: string, message: string, correlation_id: string } }`. Domain errors map to HTTP statuses via a lookup table in the router. Unhandled errors return 500 with code `INTERNAL_SERVER_ERROR`.
- **Auth selection pattern:** `server.ts` selects `MockAuthAdapter` when `MOCK_AUTH=true`, else `ClerkAuthAdapter`. Both implement `AuthPort`. The global middleware populates auth context; `requireAuthMiddleware` is applied only to the `/v1` router group.
- **Protected route group:** `v1Router.use(authAdapter.requireAuthMiddleware())` applied to the express sub-router, then `app.use('/v1', v1Router)`. Health check mounted on `app` before the v1 group.
- **Correlation IDs:** Extracted from `x-correlation-id` header or generated via `crypto.randomUUID()` per request. Included in all error responses.
- **User serialization:** `serializeUser(user)` helper in the router omits `clerkId` — `clerk_id` never appears in API responses.
- **Result type:** `Result<T>` in `packages/backend/src/shared/domain/Result.ts` — `.isSuccess`, `.isFailure`, `.value`, `.error`. Used by domain factory methods to avoid throwing.

## Domain Patterns (continued)

- **Result type pattern:** Domain factory methods (`User.create()`) return `Result<T>` — never throw. Callers check `.isFailure` and throw `.error` at the application service boundary if needed.
- **Entity factory duality:** `Entity.create(props)` validates inputs and returns `Result<Entity>`. `Entity.reconstitute(props)` skips validation — used only for DB hydration in repository adapters.
- **Mock repository in tests:** Build a plain object implementing the port interface with `vi.fn()` mocks. Override individual methods per test case. No class needed.

## Testing Patterns

- **Backend Vitest config:** `vitest.config.ts` uses `environment: 'node'`, `globals: true`, includes `src/**/*.test.ts`. Coverage excludes `src/server.ts` and test files themselves.
- **CommonJS backend with tsx dev runner:** Backend uses `"module": "CommonJS"` + `"moduleResolution": "node10"` in tsconfig for `tsc` builds. `tsx watch src/server.ts` is used in dev — tsx handles both module systems seamlessly in dev mode.
- **Tailwind CSS v4 import syntax:** Use `@import "tailwindcss"` in the CSS entry file (not `@tailwind base/components/utilities`). The `@tailwindcss/vite` plugin processes it — no `tailwind.config.*` file needed.
- **Frontend Vitest config:** `vitest.config.ts` uses `environment: 'jsdom'`, `globals: true`, `setupFiles: ['./src/test/setup.ts']`, includes `src/**/*.test.tsx` and `src/**/*.test.ts`. Setup file imports `@testing-library/jest-dom`.
- **Frontend Vitest config:** `vitest.config.ts` uses `environment: 'jsdom'`, `globals: true`, `setupFiles: ['./src/test/setup.ts']`, includes `src/**/*.test.tsx` and `src/**/*.test.ts`. Setup file imports `@testing-library/jest-dom`. Pool set to `threads` with `singleThread: true, isolate: false` to prevent OOM when loading heavy deps (Clerk/react-router) in separate workers.
- **Clerk appearance config:** `packages/frontend/src/lib/clerkAppearance.ts` exports `clerkAppearance: Appearance`. Uses raw hex values (not CSS custom properties) because Clerk's appearance prop injects inline styles. Both `SignIn` and `SignUp` share this config. Import from `@clerk/types` for the type.
- **API client factory:** `packages/frontend/src/api/client.ts` exports `createApiClient(getToken)` returning `{ get, patch, post }`. Takes a `getToken: () => Promise<string | null>` function from Clerk's `useAuth`. Throws `ApiError` with status, code, and message on non-2xx responses.
- **CSS design tokens:** Two-tier token architecture in `packages/frontend/src/styles/tokens.css`. Tier 1 identity tokens (raw values) and Tier 2 semantic tokens (component consumption). Component code consumes only Tier 2 tokens. Imported via `global.css` which is the CSS entry point in `main.tsx`.
- **Frontend tsconfig composite pattern:** Root `tsconfig.json` has `"files": []` and project references to `tsconfig.app.json` (src files, `jsx: react-jsx`, `noEmit: true`, `composite: true`) and `tsconfig.node.json` (vite/vitest configs). Use `tsc -b --noEmit` for typecheck.
- **Semantic elements over ARIA roles:** Biome's `useSemanticElements` rule requires using native semantic HTML elements instead of ARIA roles on generic elements. Use `<output>` (which has `role="status"` natively) instead of `<div role="status">`.
5 changes: 5 additions & 0 deletions .claude/manual-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ Or use the GitHub UI to open a PR from `ralph/feat-001-monorepo-scaffold` → `m
- `POSTHOG_API_KEY` — PostHog project settings > Project API Key
- All mock adapter flags default to `true` for local dev — no action needed

### MANUAL-004: Re-run migrations after merging feat-002
**Feature:** feat-002
**Action:** After merging feat-002 to main, run: `dbmate --url "$DATABASE_URL" up`
This applies the `create_users` migration.

### MANUAL-002: Start PostgreSQL
**Feature:** feat-001
**Action:** Start the PostgreSQL container: `docker-compose up -d postgres`
Expand Down
2 changes: 1 addition & 1 deletion .claude/mock-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Service | Adapter | Status | Feature | Notes |
|---------|---------|--------|---------|-------|
| Clerk | Auth | Mock available via `MOCK_AUTH=true` | feat-002 | Not yet implemented |
| Clerk | Auth | Real (with MOCK_AUTH=true fallback for tests) | feat-002 | ClerkAuthAdapter + MockAuthAdapter implemented |
| Stripe | Payments | Mock (stub) | feat-010 | Not yet implemented |
| Veriff | KYC | Mock (auto-approve) | feat-007 | Not yet implemented |
| AWS SES | Email | Mock (log only) | feat-014 | Not yet implemented |
Expand Down
Loading