# CLAUDE.md

## Agent Behavior

**Always use `pnpm`** — never `npm`, `yarn`, or `npx`. Use `pnpm exec` for local binaries, `pnpm dlx` for one-off execution.

**Never run long-running processes** (dev servers, watchers, Docker) without explicit permission.

**Read relevant docs before implementing** — never guess endpoint behavior or payload shapes.

**Use dedicated tools, not Bash** — Read (not `cat`/`head`/`tail`), Glob (not `find`/`ls`), Grep (not `grep`/`rg`), Edit (not `sed`/`awk`). This applies to all agents and subagents.

**Never run destructive git commands** — `git stash`, `git checkout -- .`, `git restore .`, `git reset --hard`, `git clean`, or any command that discards uncommitted work. If you need to check pre-existing state, use `git diff` or `git status` — never stash or discard the working tree.

## Commands

```bash
pnpm domain:build          # Required after domain changes
pnpm type-check
pnpm lint
pnpm db:migrate
pnpm db:generate
pnpm test                   # All tests
pnpm --filter @customer-portal/bff test  # BFF tests only
```

Ports: Frontend :3000 | Backend :4000/api | Prisma Studio :5555

## Architecture

```
apps/
├── portal/     # Next.js 15 (React 19, Tailwind, shadcn/ui)
└── bff/        # NestJS 11 (Prisma, BullMQ, Zod)
packages/
└── domain/     # Shared contracts, Zod schemas, provider mappers
```

**Systems of record**: WHMCS (billing, subscriptions, invoices, addresses) | Salesforce (CRM, orders) | Portal (UI + BFF orchestration)

## Key Conventions

**Imports** (ESLint enforced):

- Import from module index: `@customer-portal/domain/billing` — never root or deep paths
- Provider imports (`/providers`) are BFF-only, forbidden in Portal

**Domain**: Each module has `contract.ts`, `schema.ts`, `index.ts`, and `providers/` (BFF-only mappers). Map once in mappers, use domain types everywhere.

**Portal**: Pages in `app/` are thin shells — all logic lives in `features/` modules. No API calls in `app/`. Use `@/core/logger` not `console.log`.

**BFF**: Integration services fetch, transform via domain mappers, return domain types. Controllers are thin — use `createZodDto(schema)` + `ZodValidationPipe`. Use `nestjs-pino` logger not `console.log`.

**Validation**: Zod-first. Schemas in domain, derive types with `z.infer`. Use `z.coerce.*` for query params.

## Docs

| Topic               | Location                                       |
| ------------------- | ---------------------------------------------- |
| Overview            | `docs/README.md`                               |
| BFF patterns        | `docs/development/bff/integration-patterns.md` |
| Portal architecture | `docs/development/portal/architecture.md`      |
| Domain structure    | `docs/development/domain/structure.md`         |
| Salesforce          | `docs/integrations/salesforce/`                |
| WHMCS               | `docs/integrations/whmcs/`                     |
