# F-153 — Implementer Evidence

## Build
Feature: F-153 — Orders: associate the linked customer's email to the order read
model and display it (customer email was missing despite the user having an email).

### Changes (production)
- `src/modules/orders/domain/order.ts` — added `email?: string | null` to `OrderView`
  (read model). `Order` core aggregate is untouched (email is a denormalized read attr).
- `src/modules/orders/infrastructure/pg-order-repository.ts`:
  - added `email: string | null` to `OrderRow`;
  - added `toOrderView(row, items)` helper;
  - `findById`, `findByIdAndUserId`, `findAll`, `search` now `SELECT o.*, u.email` with
    `LEFT JOIN identity_users u ON u.id = o.user_id` (reuses the existing `search` join pattern);
  - every `OrderView` is built via `toOrderView`, so `email` is always populated
    (`string | null`; `null` when the order has no linked identity_user).
- `src/modules/orders/api/orders.routes.ts`:
  - `serializeOrder` now emits `email: order.email ?? null` (detail + list);
  - admin force-transition (`POST /orders/:id/transitions/admin`) consumes
    `order.email` (the associated email) and no longer issues a separate inline
    `SELECT email FROM identity_users` lookup → no N+1, single source of truth.

### Changes (tests)
- new `src/modules/orders/infrastructure/pg-order-repository.test.ts`:
  - `findById` resolves `email` via the `identity_users` join (asserts the query contains
    `identity_users` + `u.email` and returns the row's email);
  - returns `null` when the order has no linked identity_user;
  - returns `undefined` when the order does not exist.
- existing `OrderView` literals (`order-service`, `payments-service`, `checkout-service`)
  needed no change (`email` is optional on the view; fixtures omit it → serialized as `null`).

### Boundary / compatibility notes
- `identity_users` is referenced only as a SQL table name (pre-existing pattern in `search`);
  no TypeScript import crosses the identity↔orders boundary → `lint:boundaries` unchanged.
- No migration (`identity_users.email` already `citext NOT NULL UNIQUE`).
- Additive API field; no state-machine / endpoint / contract change.

## Evidence (commands + results)
- `npx tsc --noEmit` → **0 errors** (`TSC_EXIT:0`).
- `npx vitest run` → **200 passed | 56 skipped (50 files)**; new F-153 suite
  `orders/infrastructure/pg-order-repository.test.ts` → **3 passed**.
- `npx eslint` (touched files) → **0 errors / 0 warnings** (`ESLINT_EXIT:0`).
- `npx prettier --check` on touched files → only **pre-existing baseline warnings**
  (identical to `HEAD` for both `pg-order-repository.ts` and `orders.routes.ts`; my added
  lines are short / long string literals accepted by Prettier; no new reformatting).
- `npm run lint:boundaries` → **1 violation, pre-existing** (`security/routes` R1);
  **0 new violations** from F-153.
- `git diff --check` → **clean** (no whitespace errors).
- `./scripts/verify.sh` → **exit 0** (backlog 270 features valid; runtime-status advance to
  build/running with F-153 in_progress — see below).

## Decisions
- Resolved via the read model (Option A in ADR) rather than route-layer lookups (Option B)
  because `orders` already joins `identity_users` in SQL for `search`; generalizing that join
  to all order reads gives the email as a first-class read attribute and removes the
  per-request lookup from the admin transition.
