# AGENTS.md

This repository holds a shared core plus per client implementations. One core, many client deployments.

## Project

Stack: TypeScript, Next.js, pnpm workspaces, a headless CMS per client, per client environment configuration, GitHub Actions.

```
core/
  ui/            design system primitives, unbranded
  blocks/        composable page sections driven by CMS data
  lib/           integrations, CMS clients, analytics, forms
  config/        shared eslint, tsconfig, tailwind preset
clients/
  acme/          one directory per client
    app/         routes and pages
    theme.ts     tokens: colors, type scale, spacing
    config.ts    feature flags, integrations, locales
    content/     client owned copy and fixtures
    overrides/   client specific components that replace a core block
```

Client directories are named after the client slug and are the unit of deployment.

Entry points: `core/config/`, `clients/<client>/config.ts`, `clients/<client>/theme.ts`.

## Architecture

- A client never imports from another client. There is no shared "client utils" directory.
- `core/` never references a client by name. No `if (client === "acme")` anywhere in core.
- Customization happens in this order: theme tokens, then config flags, then a component in `overrides/`. Only if all three fail does core change.
- A core change must work for every client. If it cannot, it is a client override, not a core change.
- Adding a capability that two clients need means it goes into `core/blocks/` with a config flag, not copied twice.
- Client content lives in the CMS or in `clients/<client>/content/`, never hardcoded in a component.

## Commands

Every command targets a client.

```bash
pnpm install
pnpm --filter acme dev                 # run one client locally
pnpm --filter acme build
pnpm --filter acme test
pnpm --filter acme typecheck
pnpm --filter @core/ui test            # test shared code
pnpm lint                              # whole repo
pnpm build:all                         # build every client, run after any core change
pnpm --filter acme deploy:preview
```

A bare `pnpm dev` is not meaningful here. Always pass a client filter.

Environment files are per client: `clients/<client>/.env.local`.

## Code style

- No client name, client copy, or client asset inside `core/`.
- Colors, spacing, radii, and fonts come from `clients/<client>/theme.ts`. No raw hex values in a component.
- Feature differences are config flags in `clients/<client>/config.ts`, not conditionals in shared code.
- Core components accept props and slots. They do not read from a client config module.
- Overrides keep the same props contract as the core block they replace, so they stay swappable.
- Copy is never hardcoded in a component. Read it from CMS data or the client content directory.
- Third party scripts, pixels, and analytics are declared in client config and loaded through the core loader.
- Absolute imports through the workspace alias. No relative paths crossing a package boundary.

## Boundaries

Do not touch without explicit instruction:

- Any other client's directory when working on a scoped task. Stay inside the client you were asked about.
- `core/` when the task is a single client request. Propose the core change instead.
- Client credentials, API keys, CMS tokens, or `.env` files of any kind.
- Production CMS content and any live data.
- `.github/workflows/`, deployment configuration, DNS, and domain settings.
- `pnpm-lock.yaml`.

Needs human review: every change under `core/`, any new third party integration or tracking script, anything touching a client's payment, contact form, or consent handling, and any dependency upgrade.

If a client request seems to need a core change, write down the change, list which clients it affects, and stop.

## Testing

- `pnpm --filter <client> test` for client scoped work.
- A change under `core/` requires `pnpm build:all` plus the test suites of the smoke test clients listed in `core/SMOKE_CLIENTS.md`.
- Required tests: core blocks with logic, form handling, and every integration in `core/lib/`.
- Client code needs tests only where it contains logic. A themed page composition does not.
- Visual regression runs per client in CI. Do not accept new baselines without looking at the diff.

## Git workflow

- Branch as `client/<slug>/short-description` for client work, `core/short-description` for shared work.
- Conventional commits with the client or package as scope: `fix(acme): correct VAT display on invoices`, `feat(core-blocks): add pricing table variant`.
- Never mix a core change and a client change in one commit.
- PR title starts with the client slug or `core`. The description states which clients are affected and whether a redeploy is needed.
- A core PR lists the clients that were smoke tested.
- Never push to `main`.
