--- name: add-prisma-model description: Add a new Prisma model to prisma/schema.prisma following the repo's multi-tenant conventions, then regenerate the client, create a migration, and refresh the schema map. Use when the user asks to add a model, table, entity, schema, or new database object. --- # Add a new Prisma model Every business model in this repo is multi-tenant (scoped by `organizationId`), uses `cuid()` IDs, has `createdAt` / `updatedAt`, and gets indexed on its FK columns. The schema map (`docs/SCHEMA_MAP.md`) is a generated artifact — refresh it after schema edits. ## Recipe ### 1. Edit `prisma/schema.prisma` Add the model near related models (keep the section comment dividers intact). Template for a typical business model: ```prisma model Widget { id String @id @default(cuid()) organizationId String name String description String? config Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) @@index([organizationId]) @@index([organizationId, name]) } ``` Then add the back-relation on `Organization`: ```prisma model Organization { // ...existing fields... widgets Widget[] } ``` If the new model is owned by a user (e.g. a comment), add `userId` + `@relation` + `onDelete: Cascade` (or `SetNull`, depending) and the back-relation on `User`. Auth tables (`User`, `Account`, `Session`) and `Organization` itself are the only models without `organizationId`. ### 2. Regenerate the Prisma client ```bash npx prisma generate ``` This rewrites `src/generated/prisma/`. The client is consumed via `import { prisma } from "@/lib/db"` — no other file should change. ### 3. Create a migration For DB changes you intend to keep: ```bash npx prisma migrate dev --name add_widget_model ``` For prototype-stage edits (no migration yet): ```bash npm run db:push ``` Once you're settling on a shape, switch to migrations — `prisma/migrations/` is append-only and authoritative for production. ### 4. Refresh the schema map ```bash npm run schema:map ``` This regenerates `docs/SCHEMA_MAP.md` from `prisma/schema.prisma`. New models land in the **Other** group by default. To categorize: 1. Open `scripts/generate-schema-map.ts`. 2. Add the model to the right `MODEL_GROUPS` bucket (or add a new bucket). 3. Re-run `npm run schema:map`. ### 5. Wire it into API + UI - API routes: follow the `add-api-route` skill. Every query MUST scope by `organizationId`. - Activity log: if the model represents user-meaningful state, fire `logActivity(...)` from `src/lib/activity-log.ts` on create / update / delete. - Integrations: if the model should propagate to Planning Center / Monday / etc., add an event in `src/lib/integrations.ts` and a handler in the relevant provider under `src/lib/integrations/providers/`. ## Common patterns by model shape ### Soft delete / archive Don't introduce a `deletedAt` column unless there's a real use case — this repo uses hard delete + `ActivityLog` for audit trail. If you do need soft delete, add `archivedAt DateTime?` (not `deletedAt`) and remember to filter `archivedAt: null` in every list query. ### JSON config column Lots of models have a `Json?` column (`Integration.config`, `ResponseCard.fieldData`, etc.) — perfect for variable shapes. Treat as untrusted on read; validate / coerce before use. Don't try to query inside JSON with raw SQL. ### Denormalized display field If you add a model with a denormalized display string (like `ResponseCard.name`), establish the recompute path in the SAME PR — both server-side (any write path) and any UI that reads it. See the cautionary tale in `AGENTS.md` § 4. ### Lookup table for enums Prisma enums exist but this repo doesn't use any. Convention here: `String` column with documented allowed values + a TypeScript union in `src/lib/.ts`. Stay consistent. ## Anti-patterns - Forgetting `organizationId` → cross-tenant data leak waiting to happen. - Hand-editing a previous migration → `prisma/migrations/` is append-only; create a new migration. - Editing files under `src/generated/prisma/` → regenerate instead. - Adding `@@map("...")` to a single model → none of the existing models use it; keep the schema consistent. - Skipping `npm run schema:map` after adding the model → `docs/SCHEMA_MAP.md` drifts immediately. - Adding a back-relation on `Organization` without thinking about cascade behavior → `onDelete: Cascade` is usually right; `SetNull` is sometimes right; `Restrict` almost never.