echos-ocr/AGENTS.md

69 lines
6.1 KiB
Markdown
Raw Normal View History

Bootstrap agent-context pipeline (L1 + L2 + L3) 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>
2026-05-23 15:55:18 -04:00
# AGENTS.md — AI collaboration (Echo OCR)
Guidance for agents and humans working in this repo. Prefer existing patterns over new abstractions.
## 1. Project overview
Echo OCR is a Next.js + Prisma app for Echo Life Church. It ingests paper "connect cards" via OCR (scanned PDFs / images / public survey submissions), extracts structured data, stores it in PostgreSQL behind a multi-tenant org model, and serves a filterable review UI with integrations to Planning Center, Monday.com, Airtable, and webhooks.
- **Framework:** Next.js 16 (App Router) + React 19, TypeScript strict
- **Data:** Prisma 7 (custom client output at `src/generated/prisma/`) + PostgreSQL via `pg` + `@prisma/adapter-pg`
- **Auth:** NextAuth 5 beta — session in `src/auth.ts`; server-side `auth()` wrapped by `requireApiAuthWithOrg` in `src/lib/api-auth.ts`
- **UI:** Tailwind v4 + shadcn/ui primitives in `src/components/ui/`; `lucide-react` icons; `sonner` toasts
- **OCR / AI:** Ollama vision models (local) and the `ai` SDK with OpenAI-compatible gateways
- **Storage:** S3-compatible (MinIO local, anything S3 in prod) via `@aws-sdk/client-s3`
- **Hosting:** Vercel (`vercel.json`) primary; Dockerfile + `docker-compose.yml` for Coolify / local
## 2. Architecture quick reference
| Area | Path | Notes |
| --- | --- | --- |
| App pages | `src/app/(dashboard)/`, `src/app/(auth)/`, `src/app/(marketing)/` | Route groups; dashboard is the protected app shell |
| API routes | `src/app/api/**/route.ts` | All start with `requireApiAuthWithOrg()`; errors via `handleApiError` |
| Prisma client | `src/lib/db.ts` | Lazy proxy singleton; **import `prisma` from `@/lib/db`** (never instantiate `new PrismaClient()`) |
| Auth helpers | `src/lib/api-auth.ts`, `src/auth.ts` | `requireApiAuthWithOrg(action?)` returns an `OrgSession` with `user.id` + `user.orgId` |
| Permissions | `src/lib/permissions.ts` | `can(role, action)` and `Action` union; roles: `owner > admin > editor > reviewer > viewer` |
| UI primitives | `src/components/ui/` | shadcn-style; do not duplicate — extend or compose |
| Feature components | `src/components/cards/`, `src/components/forms/`, etc. | Co-located by feature |
| OCR pipeline | `src/lib/ocr.ts`, `src/lib/ai-ocr.ts`, `src/lib/ollama.ts` | Background job model in `ProcessingJob` |
| Integrations | `src/lib/integrations/providers/` | Each provider exports the same shape; fired via `fireIntegrationEvent()` |
| Schema | `prisma/schema.prisma` | 21 models, no enums; multi-tenant via `organizationId` |
| Schema map | `docs/SCHEMA_MAP.md` | Regenerate with `npm run schema:map` |
Generated Prisma client lives at `src/generated/prisma/` — do not edit; regenerate with `npx prisma generate`.
## 3. Key conventions
- **Auth (server):** `const session = await requireApiAuthWithOrg(); /* session.user.orgId */`. Pass an optional `Action` to enforce a permission inline; `handleApiError` returns the right 401/403/500.
- **Multi-tenancy:** every `ResponseCard`, `FormTemplate`, `Person`, etc. is scoped by `organizationId`. **Always** include `organizationId: session.user.orgId` in `where:` filters and create payloads — there is no row-level security in dev.
- **API errors:** wrap handlers in `try { ... } catch (error) { return handleApiError(error); }`. Don't hand-roll `NextResponse.json({ error }, { status: 500 })`.
- **Validation:** body parsing is hand-rolled with defensive defaults (see `src/app/api/cards/[id]/route.ts` PUT). Zod is in `package.json` but not widely adopted yet — match the surrounding file's style.
- **Form templates:** field keys that match a top-level `ResponseCard` column must be `isCore: true` (see `src/lib/form-templates.ts`). Non-core values live in `ResponseCard.fieldData` JSON; the PUT route auto-promotes matching keys to columns.
- **Toasts:** `import { toast } from "sonner"``toast.success`, `toast.error`, `toast.info`.
- **Imports:** `@/*``src/*` (`tsconfig.json` `paths`). Generated Prisma at `@/generated/prisma/...`.
- **File names:** kebab-case for routes (`reprocess-batch/`), kebab-case for components (`upload-modal.tsx`), camelCase for hooks (`useUserProfile`).
- **Client vs server:** every interactive page starts with `"use client"`; API routes never include it.
## 4. Common gotchas
- `ResponseCard.name` is a **denormalized** display string. The PUT route at `src/app/api/cards/[id]/route.ts` recomputes it from `firstName + lastName` on edit; the OCR pipeline and `survey/submit` populate it on create. If you write a new path that mutates first/last, recompute `name` too.
- Prisma client lives at `src/generated/prisma/` (custom output, not the default `@prisma/client`). Import enums and types from there.
- `src/lib/db.ts` exports a **lazy `Proxy`** so importing `prisma` doesn't open a DB pool at module load — required for Next.js build-time data collection without `DATABASE_URL`. Don't call `prisma` from top-level module code.
- MinIO/S3 images are served through `GET /api/images/[...path]` which presigns and proxies. Don't hand back raw S3 URLs to the client.
- The Cloudflare/Vercel route `src/middleware.ts` enforces auth before requests hit `src/app/...`. New unauthenticated routes must be allowlisted there.
- `prisma/schema.prisma` does **not** declare a `url`. The connection string comes from `DATABASE_URL` at runtime — local dev needs `.env`.
## 5. Running locally
- **Runtime:** Node 18+ (Node 20 LTS recommended).
- **Setup:** see [`README.md`](README.md) (`docker compose up -d postgres minio`, `npm run db:push`, `npm run dev`). Requires Ollama with a vision model (`ollama pull llava:7b`) and GraphicsMagick for PDF rasterization.
- **Dev server:** `npm run dev`. App at `http://localhost:3000`, MinIO console at `:9001`.
## 6. Testing
- **Runner:** none configured yet. There are no unit, integration, or E2E tests. Adding tests is welcome — start with `vitest` and `@playwright/test`; gate them in CI before requiring green.
## 7. Deployment
- Primary target is Vercel (`vercel.json`). `Dockerfile` + `docker-compose.yml` are for Coolify / self-host. Build step is `npx prisma generate && next build` (see `package.json`). Notes on Coolify / Postgres / MinIO / Traefik wiring are in [`server_deploy.md`](../server_deploy.md) at the workspace root.