# CLAUDE.md

See @GUIDELINES.md for the full project coding guidelines. All rules there are mandatory.

## Module handbook

When **building or documenting a module/submodule**, first read
`docs/modules_handbook/README.md` and follow its conventions for that module's `readMe.md` —
one folder per module (`<module>/readMe.md`), related modules grouped under a shared parent
folder, the three core sections (What it does / How it works / Related files), and a
**`Reference usage`** section for shared services (e.g. `MediaService`).

## Member-facing learning copy

Before writing or editing any screen a MEMBER reads to learn (the DMAIC road, the Glossary
course, lesson blurbs, AI Coach teaching copy, Area Tutorial scripts), read
[`docs/modules_handbook/main/dmaic-road/content-rules.md`](docs/modules_handbook/main/dmaic-road/content-rules.md)
and run its **first-time reader test** on the screen: every term, letter, name and number must be
answerable from this screen or an earlier one on the same path — define on first use, never
reference forward by acronym, introduce a character before quoting them. The rules came from a
founder review (2026-09-02) of copy that assumed the reader had read the book.

## Using a shared service

Before calling a shared service from a feature, **read its handbook `readMe.md` first** — each
has a *Reference usage* section showing the canonical way to use it, so every consumer stays on
the same patterns instead of re-inventing them:

- **List downloads (Excel / CSV) — the `ExportsResource` + `ExportMenu` PATTERN** (not a service:
  one export class per list, the shared trait names the file, `EscapesCsvFormulas` makes it safe
  to open) → [`docs/modules_handbook/shared/export/readMe.md`](docs/modules_handbook/shared/export/readMe.md)
- **File storage / uploads — `MediaService`** (GCS-backed `Src\Common\Media`; signed URLs,
  collections) → [`docs/modules_handbook/shared/media/readMe.md`](docs/modules_handbook/shared/media/readMe.md)
- **AI calls — `AiClient`** (provider-agnostic chat/prompt, prompt-key registry, `ai_requests`
  logging, the resilient `AiJob` queue lane) → [`docs/modules_handbook/shared/ai/readMe.md`](docs/modules_handbook/shared/ai/readMe.md)
- **White-label AI routing + chargeback — the PropertyLab AI Gateway** (`AI_GATEWAY_MODE=client`
  relays every AI call to the Hub and bills credits; `AI_GATEWAY_HUB_ENABLED` serves it. Touch
  nothing that shows a provider, model or dollar figure for AI without reading this)
  → [`docs/modules_handbook/ai-gateway/readMe.md`](docs/modules_handbook/ai-gateway/readMe.md)
- **SMS — `SmsSender`** (provider-agnostic interface over SMS360; every send costs money, the
  gateway is IP-whitelisted, and it is synchronous — queue anything bulk)
  → [`docs/modules_handbook/shared/sms/readMe.md`](docs/modules_handbook/shared/sms/readMe.md)
- **Voice calls — `VoiceCaller`** (provider-agnostic interface over Twilio Programmable Voice;
  plays a pre-rendered AI-voiced audio, never conversational. Every answered call is billed per
  minute, calls only 9am–9pm, voicemail is hung up on — queue + pace anything bulk)
  → [`docs/modules_handbook/shared/voice/readMe.md`](docs/modules_handbook/shared/voice/readMe.md)
- **AI conversation calls — `ConversationalCaller`** (real-time two-way AI voice agent over
  Retell; the agent listens and talks back — the sibling of `VoiceCaller`, never a variant of
  it. Billed per minute by Retell AND Twilio; the agent must disclose it is an AI (Retell ToS);
  same 9am–9pm window; the CRM keeps its own transcript record in `ai_voice_calls`)
  → [`docs/modules_handbook/shared/voice-agent/readMe.md`](docs/modules_handbook/shared/voice-agent/readMe.md)
- **Email — Mailables + `EmailSender`** (two paths: a Mailable per transactional email via the
  `Mail` facade; the provider-agnostic `Src\Common\Email\EmailSender` interface for automation
  sends — never throws, returns bool) → [`docs/modules_handbook/shared/email/readMe.md`](docs/modules_handbook/shared/email/readMe.md)
- **Delivery credentials — `MessagingCredentialProvider`** (DB-first SMS/SMTP/GetResponse
  credentials, managed on Messages → Settings → Delivery APIs; consumers just read `config(...)`
  — never call the provider directly)
  → [`docs/modules_handbook/shared/messaging-credentials/readMe.md`](docs/modules_handbook/shared/messaging-credentials/readMe.md)
- **Activity trail — `ActivityLogger`** (records what a person DID, shown on Leads → Show;
  one call, never throws at its caller, coalesces repeated edits; NOT `Src\Calendar\Activity`,
  which is a calendar entry) → [`docs/modules_handbook/shared/activity-log/readMe.md`](docs/modules_handbook/shared/activity-log/readMe.md)
- **Phone alerts to STAFF — `Notifier`** (`Src\Common\Notify`; push an operational message to
  an admin's phone via Telegram. Callers name a registered *event* — never a person or a
  channel; admins pick their own destinations + subscriptions on Manage → Notifications.
  Queued, throttled, fail-soft, every attempt logged. This is for alerting colleagues — customer
  messaging is WhatsApp/SMS) → [`docs/modules_handbook/shared/notify/readMe.md`](docs/modules_handbook/shared/notify/readMe.md)

---