Installs a 3-layer Cursor-aligned agent pipeline so future agent
sessions can orient quickly and stay inside guard rails:
L1 — context for any agent reading the repo:
- AGENTS.md (top-level orientation, conventions, no-go zones)
- .cursor/rules/ (no-go-zones, api-routes, prisma, prisma-schema-map)
- .cursor/skills/ (add-api-route, add-prisma-model)
- docs/SCHEMA_MAP.md generated from prisma/schema.prisma
- scripts/generate-schema-map.ts (regenerate the map; wired up as
`npm run schema:map`)
L2 — subagent roles for the 9-stage idea-to-feature pipeline:
- .cursor/agents/role-*.md (conductor, architect, ia-architect,
design-system-auditor, implementer, reviewer, ux-reviewer,
a11y-auditor, doc-writer) with explicit multitask annotations.
L3 — pipeline scaffolding:
- .github/CODEOWNERS, PR template, and CI workflows (ci.yml,
preview-smoke.yml, visual-diff.yml, pr-health-rollup.yml).
Test job is intentionally disabled until Playwright is wired up.
- .convoys/ folder for per-feature run notes + scripts/log-convoy-event.sh.
- scripts/wt.sh worktree helper.
- src/lib/flags/index.ts simple env-driven feature flag wrapper.
- tests/smoke/app.smoke.spec.ts (Playwright smoke; excluded from
tsc until @playwright/test is installed — see tsconfig change).
Also writes .agent-context-manifest.yml so the sync-agent-context
skill can detect drift and offer selective updates from upstream.
Follow-ups (not in this commit):
- Install @playwright/test and re-enable the test job in ci.yml.
- Review .cursor/agents/role-*.md and trim any roles that don't
apply to this codebase.
Co-authored-by: Cursor <cursoragent@cursor.com>
4.5 KiB
| name | description |
|---|---|
| add-prisma-model | 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:
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:
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
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:
npx prisma migrate dev --name add_widget_model
For prototype-stage edits (no migration yet):
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
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:
- Open
scripts/generate-schema-map.ts. - Add the model to the right
MODEL_GROUPSbucket (or add a new bucket). - Re-run
npm run schema:map.
5. Wire it into API + UI
- API routes: follow the
add-api-routeskill. Every query MUST scope byorganizationId. - Activity log: if the model represents user-meaningful state, fire
logActivity(...)fromsrc/lib/activity-log.tson create / update / delete. - Integrations: if the model should propagate to Planning Center / Monday / etc., add an event in
src/lib/integrations.tsand a handler in the relevant provider undersrc/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/<area>.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:mapafter adding the model →docs/SCHEMA_MAP.mddrifts immediately. - Adding a back-relation on
Organizationwithout thinking about cascade behavior →onDelete: Cascadeis usually right;SetNullis sometimes right;Restrictalmost never.