Imported from ronronoa/agapay (
docs/AGENTS.md). Install upstream withnpx skills add ronronoa/agapay --skill docs. Copyright stays with the author.
AGENTS.md — Agapay
Calamity relief donation and distribution system. npm workspaces monorepo, TypeScript end to end.
Keep this file short. Detail lives in docs/; load it only when the task needs it.
Do not explore the repo
You do not need to scan the project. Everything you need to orient is below.
- Read
docs/STATUS.mdfirst. It says what is built, what is in progress, and what is next. - Use the Where things live table to go straight to the right folder.
- Search with targeted
grep/glob inside one module folder. Never list or read the whole tree,node_modules,dist, lockfiles, or generated Prisma output. - Read at most the files you will change plus their direct imports. If you need more, say why.
- Do not re-read a doc you already read in this session.
- When you finish, update
docs/STATUS.md(3 lines max: what changed, what is next).
Stack
apps/web: React + Vite + TypeScript, React Router, TanStack Query, React Hook Form + Zod, Tailwind + shadcn/uiapps/api: Node LTS + Express 5 + TypeScript, Prisma + PostgreSQL, Zod, pino, pg-boss (jobs)packages/shared: Zod schemas, enums, DTO types, status messages. Only place web and api share code- Email: Resend behind
EmailProvider(default adapter isconsolein dev). Calendar: Google Calendar via service account +.ics - Chatbot: OpenRouter via Vercel AI SDK, read-only tools only
Commands
npm run dev web + api (API reads ../../.env; needs a Postgres for anything past /health)
npm run typecheck npm run lint npm test npm run test:e2e
npm run db:migrate npm run db:seed npm run build
Run npm run typecheck && npm run lint && npm test before saying a task is done. Report what you actually ran. If a command does not exist yet, say so; do not invent output.
Where things live
| Task | Go to |
|---|---|
| New or changed endpoint | apps/api/src/modules/<module>/ (*.routes.ts, *.controller.ts, *.service.ts) + Zod schema in packages/shared/src/<module>.ts + update docs/api.md |
| Business rule or status change | <module>.service.ts only, never a controller |
| DB table or column | apps/api/prisma/schema.prisma + new migration + update docs/database.md |
| Inventory math | apps/api/src/modules/inventory/ only |
enqueue via notifications service; templates in apps/api/src/modules/notifications/templates/ |
|
| Background job | apps/api/src/jobs/ |
| Chatbot | apps/api/src/modules/chat/ (prompts, tools, quota) and apps/web/src/features/chat/ |
| Page / screen | apps/web/src/features/<feature>/ and route in apps/web/src/routes/ |
| Shared UI component | apps/web/src/components/ui/ (one StatusBadge for all statuses) |
| Env var | apps/api/src/lib/env.ts (Zod) + .env.example |
Modules: auth users item-types campaigns donations inventory requests distributions calendar notifications reports public chat audit.
Flow inside the API: route → controller → service → Prisma. Modules call each other through services, not tables.
Docs (read only when relevant)
| File | Read it when |
|---|---|
docs/STATUS.md |
Always, first |
docs/rules.md |
Before writing code in a new area; its business rules have IDs (BR-xxx) |
docs/rules-backend.md |
Any change in apps/api or packages/shared. Cite rule IDs (TS-xx, DB-xx, SEC-xx) in PRs |
docs/api.md |
Adding or changing an endpoint, payload, or error code |
docs/database.md |
Touching schema, constraints, codes, inventory recipes |
docs/architecture.md |
State machines, sequences, auth, jobs, deployment questions |
docs/chatbot.md |
Any chat work |
docs/design.md |
Any UI work |
docs/PLAN.md |
Scope, milestones, decision IDs (D-xx). Not needed for routine tasks |
If code and docs disagree, stop and fix one in the same change. Do not leave drift.
Hard rules (the short list)
- Do not invent endpoints, tables, statuses, fields, env vars, or business rules. Ask or propose a doc change.
- Ask before changing: Prisma schema, auth, inventory arithmetic, email templates, public endpoints, anything touching personal data.
- Validate every input with the shared Zod schema. No
any. Types come fromz.infer. - Only services change state. Multi-row changes run in one
prisma.$transaction. - Stock changes use the atomic
UPDATE ... WHERE available >= qtypattern and always append anInventoryMovement. Never read-then-write stock. - Never send email or call Google/OpenRouter inside a request. Insert an outbox row in the same transaction and let the worker send.
- Never hard-delete donations, requests, distributions, movements, or audit logs.
- Public endpoints live under
/public, are rate limited, return minimal DTOs, and give one genericNOT_FOUNDon failed lookups. A reference number alone never grants access. - Never put personal data (name, email, phone, notes) in logs, URLs, audit metadata, or chat prompts.
- Chatbot tools are read-only. Persona and tools are chosen server-side from the auth role. Admin tools return aggregates only.
- UI: every view has loading, empty, error, success states; keyboard operable; status is never shown by colour alone; use design tokens, not raw hex. No dead buttons or links to pages that do not exist.
- No fabricated content (stats, testimonials, partners, legal text). Use
TODO(owner): .... - No new dependency without stating why and the alternative considered.
- No secrets or real personal data in the repo. Use
.env.exampleplaceholders. - Backend types: no
any, no unsafeas; statuses are discriminated unions with a transition table and an exhaustiveswitch(assertNever). TypedAppErrorwithcause; never swallow errors (TS-03 to TS-13). - State changes use a conditional update on the expected current status (
updateManywithwhere: { id, status },count === 0→409). No read-then-write (DB-09, DB-13). - Transactions are short: no email, HTTP, or LLM calls inside one. Inject
Clock; never callnew Date()in business logic (DB-11, TS-23). - Every outbound call has a timeout and bounded retry. No floating promises (TS-16, TS-17).
- Constraints belong in the database as well as in code. Migrations are expand/contract and never edited after applying (DB-01, DB-24, DB-25).
- Tests use a real Postgres, not mocked Prisma. Add a concurrency test for any locking or conditional-update logic (TEST-01, TEST-03).
Output style
- Make the smallest change that solves the task. One concern per change.
- Comments explain why, not what.
- If requirements are ambiguous, ask one specific question before coding.
- Final message: what changed, files touched, commands run and their result, docs updated, what is next.
