Audience: AI coding agents first. Work directly in this repository; do not require another agent, CLI, or model.

# Platform module build rules

Build `@platform-modules/*` packages from the fixed boundary spec and plan. Preserve the repository's architecture and coding doctrine.

**Before writing any code, read — in this order:**
1. The module's **boundary spec** — `docs/specs/<date>-<module>-…-boundaries.md` (the source of truth).
2. The module's **plan** — `docs/plans/<date>-<module>-module.md` (wave plan + your TDD tasks).
3. `docs/standards/coding-standard.md` — the §2 ladder, R1/R2/R3 porting hazards, the §4 seam checklist, §6 `-react` packaging.

**Spec-first (hard rule):** the spec is authoritative; code conforms to it, never the reverse. If you find the spec **wrong, ambiguous, or incomplete** mid-build, **STOP** and report it; amend the spec and update the plan *before* resuming. **Never** silently "fix" by diverging from the spec; a green diff that contradicts its spec is a defect.

**How you build:** in a `.worktrees/` copy (IRON LAW — keep `main` churn-free). One behavioral test per export (fallback + edge inputs, not the happy line). Honor the hard floor on every rung — trust-boundary validation, data-loss handling, security, accessibility, money-grade idempotency — laziness lives *above* that floor, never through it. Address every build/test/commit signal (a `WARN`, a type hint, a hook notice) before moving on; green means *clean*, not *passed-with-noise*.

**Audit-hardened prior-app code (coding-standard §5.1 / R4):** when the boundary spec marks a path **audit-hardened** (money · refund · webhook · session · password — fixes already baked in), port its body **byte-faithfully — preserve, do not re-derive or "tidy"**. Re-deriving re-introduces audit-fixed bugs you cannot re-find. Carry its `secaudit-*` conformance test. If the seam can't wrap the body unchanged, **STOP** and surface a ported-vs-source diff — never a silent rewrite.

## Learned Rules

### consumer-fixture-test-suffix | fired:2 | 2026-06-15
Named a consumer vitest fixture `apps/consumer/tests/<mod>.spec.ts` → wrong. Playwright `testMatch:'**/*.spec.ts'` also grabs it → `Vitest failed to access its internal state` crash (recurred uploads+helpdesk). Repo convention: `.test.ts`=vitest, `.spec.ts`=playwright.
Prevent: name every vitest file `.test.ts`; reserve `.spec.ts` only for files that import `@playwright/test`.

### vitest-green-not-typecheck-green | fired:1 | 2026-06-15
Imported `node:fs`/`node:path`/`node:url` in a test with no `@types/node` in the workspace → wrong. `vitest run` passes (runtime has node) but `tsc --noEmit` is RED → ships a red `pnpm gate`. packages/helpdesk/src/case.test.ts.
Prevent: no `node:` builtins in module/test code — use web-standard (`import.meta.url`, `URL`, `crypto.subtle`); run `pnpm --filter <pkg> typecheck` before declaring a task green, never just the tests.

### no-process-env-in-host-agnostic-client | fired:1 | 2026-06-25
Gated a dev-only warn on `globalThis.process?.env?.NODE_ENV !== 'production'` in a host-agnostic `-react` package → wrong both ways: in the intended browser/edge runtime `globalThis.process` is `undefined`, so `undefined !== 'production'` is TRUTHY → the "dev-only" branch FIRES in prod; and it is not DCE-eliminable → the warn string ships in the consumer bundle. packages/billing-react/src/StripePaymentProvider.tsx (deleted, superseded by internal `key=`).
Prevent: never read `process.env` / `globalThis.process` / `NODE_ENV` in `-react` or client-runtime `src` (process is absent on browser/edge — the kit's target hosts). Env reads belong only in build configs (`tsup`/`vitest`/`*.config.ts`). Prevent footguns BY CONSTRUCTION (e.g. internal `key={clientSecret}` auto-remount), never by a runtime dev-warn.
@CLAUDE.md
