# 02 — Next.js architecture

## Stack

- **Next.js 15** (App Router), **React**, **TypeScript**
- Route handlers under `app/api/`
- OpenAI SDK: chat `gpt-4o-mini`, embeddings later `text-embedding-3-small` (today: existing `local-hash-v1` files)
- Session: encrypted cookie or server session storing `caseId`
- Profile persistence: JSON file or SQLite for v1 (`data/cases/{caseId}.json`); Postgres later

Suggested app root: `apps/mortgage-chat/` **or** `web/` at repo root. Keep RAG files where they are (`data/knowledge`, `data/verticals`).

## Information flow

```
Customer message
    → POST /api/mortgage/chat
    → load ApplicantProfile + processStep
    → retrieve RAG (mortgage knowledge + mortgage vertical; BMI/health only if step 3/4)
    → gpt-4o-mini (reply + structured field patches)
    → merge patches into profile
    → recompute step from exit gates
    → return { assistantMessage, profile, process, citations }
```

## App Router tree (planned)

```
web/
  app/
    layout.tsx
    page.tsx                      # landing → start mortgage chat
    mortgage/
      page.tsx                    # three-pane: chat | profile | process
    api/
      mortgage/
        chat/route.ts
        profile/route.ts          # GET current profile
        case/route.ts             # POST create case
  lib/
    rag/
      retrieve.ts                 # cosine vs embeddings.json
      stores.ts                   # paths to knowledge vs verticals
    mortgage/
      schema.ts                   # ApplicantProfile zod
      gates.ts                    # step 0–11 from filled fields
      extract.ts                  # parse model JSON patches
    openai.ts
  components/
    ChatPane.tsx
    ProfilePane.tsx
    ProcessStepper.tsx
```

## API contracts

### `POST /api/mortgage/case`

Creates `{ caseId }`. Empty profile, `processStep: 0`.

### `GET /api/mortgage/profile?caseId=`

Returns profile + field completeness vs dataset R/S/O.

### `POST /api/mortgage/chat`

```json
{
  "caseId": "uuid",
  "message": "We still owe about 240000 on a 30 year loan"
}
```

Response:

```json
{
  "reply": "Thanks — I’ll note remaining balance about $240,000…",
  "profile": { },
  "process": {
    "step": 2,
    "stepKey": "step-2",
    "label": "Core demographics & loan facts",
    "exitGate": "Age + loan_balance present",
    "missingRequired": ["date_of_birth"]
  },
  "citations": [{ "source_path": "...", "title": "..." }],
  "model": "gpt-4o-mini"
}
```

## Environment

```
OPENAI_API_KEY=
OPENAI_CHAT_MODEL=gpt-4o-mini
KNOWLEDGE_MORTGAGE=./data/knowledge/mortgage/embeddings.json
VERTICAL_MORTGAGE=./data/verticals/mortgage/embeddings.json
KNOWLEDGE_BMI=./data/knowledge/bmi/embeddings.json
VERTICAL_BMI=./data/verticals/bmi/embeddings.json
KNOWLEDGE_HEALTH=./data/knowledge/health/embeddings.json
VERTICAL_HEALTH=./data/verticals/health/embeddings.json
```

GPT-4o-mini is called **only on the server**. Never expose the API key to the browser.

## Security

- No SSN until step 10 and only on an approved channel.
- Do not log full health answers if `LOG_QUERIES=false`.
- Customer messages may contain health data at step 4 — treat as sensitive.
