# build-schema — Analysis → reviewed schema (DESIGN-LED)

> **Builds:** the project's schema JSON (`database/schema/<feature>.json` + `features.json`), from a
> **complete, design-led analysis**, ready for the developer to review on `/schema-designer`.
> **Runs BEFORE** [`build-database.md`](build-database.md). This file exists because the schema is the
> step most likely to go wrong when the design isn't fully read or existing tables aren't reviewed.
> **Inputs:** `docs/project/brand-identity.md` (Figma link), `docs/project/analysis.md`, the **Figma design**, and the **existing base schema**.

---

## 0) Hard rules (the mistakes this prevents)
1. **The design is mandatory.** You MUST walk the **whole** Figma file, **screen by screen** — not the PDF/brief alone. Fields, states, and flows come from the screens. **When the project has multiple designs (e.g. a user app + a provider app), read EVERY one fully** — but the output is still **ONE unified schema / one database** for the whole project (both apps share the backend). Tag each entity **shared vs role-specific**, but never split the schema per audience — the audience distinction lives only in the API/plans layer, not the DB.
2. **Reuse before create.** You MUST review the existing base schema first and never duplicate a table that already exists.
3. **Files are never omitted.** Every document/image/file must be represented explicitly (media table or a dedicated files table).
4. **Stop on partial input.** If you cannot access the full design (or it's incomplete), STOP and ask — do not generate a schema from a partial picture.
5. **Study the whole project BEFORE anything.** Before producing the schema, the plans, or the database, study the **entire** project — every screen + the full analysis + the full existing schema — until you hold a complete mental model. Never start the schema/plans/DB from a partial read.
6. **Complete coverage (no gaps).** The output must cover **everything**: every entity/column, every screen action → its endpoint, and **every admin-managed table → its own `cruds/<entity>.md`**. Build a coverage checklist (all tables + all actions ticked against their plan/code) and explicitly flag anything intentionally skipped, with the reason. A missing CRUD/endpoint/table = not done.

---

## 1) Read the WHOLE design — screen by screen
Use the Figma MCP (`get_metadata` to enumerate; **`get_screenshot` is the cheap default for reading Arabic labels at breadth** — prefer it over `get_design_context`, which is for deep per-component detail). Get the file key/URL from `brand-identity.md`; if that file is still an unfilled template, ask the developer for the Figma URL. **Start from the node-id in the project's Figma URL** (e.g. `0:1`), not necessarily the first listed page — the real screens may live on a page the cover doesn't surface.

**Figma access — pick the server that's actually connected:**
- **Cloud / fileKey-based MCP (preferred — works headless):** its tools take a `fileKey` + `nodeId` directly. Extract `fileKey` from the URL (`figma.com/design/<fileKey>/…` or `/file/<fileKey>/…`) and `nodeId` from `?node-id=<id>` (some tools want `:` instead of `-`).
- **Desktop Dev Mode MCP:** reads the file **open in the local Figma desktop app** — needs the app running with **Dev Mode on**. If only this one is connected and nothing is open, **ask the developer to open the file in Figma desktop (Dev Mode)** or to enable the fileKey-based server.
- If no Figma server responds at all → **STOP and ask**; never proceed design-blind.
- **Enumerate every screen — no cap, no target number.** Read ALL of them whatever the count (often 30+, sometimes far more); the "30+" is an example of typical size, not a limit. Robust enumeration:
  - If a page/canvas's `get_metadata` returns **empty**, screenshot the board (`maxDimension` high) and drill into frames.
  - If `get_metadata` is **too large** (overflows the tool-result limit), save it to a file and extract frame/section node-ids with a script. Frame/section names often come back as **UTF-8-as-Latin1 mojibake** — decode them before use.
- For **each** screen record: name/purpose, every input/field (+ implied type), every list/column, every action/button, every status/state, validation hints, and which entity/API it touches.
- **Same-named screens are NOT duplicates.** When two or more screens share a name (e.g. "order details"), never collapse them by name — dump each screen's full content and **diff them field by field**. They're usually distinct states / variants / roles (e.g. order details for `new` vs `delivered`, or customer vs provider view). Map each as its own row and note exactly what differs.
- **One screen → many actions, not one API.** A single screen often triggers several operations (e.g. accept / reject / mark-received / mark-prepared / cancel). Enumerate **every** action, button, and state-transition; **each becomes its own endpoint/operation** in the APIs list. Never reduce a screen to a single API, and capture the **status model** those actions imply (the set of states + the allowed transitions → an Enum + the endpoints that move between them).
- **Multi-step (wizard) flows — NEVER collapse the sequence.** When one task spans several screens as an **ordered sequence** (each screen = a step), map **every step as its own endpoint** and record the order + what advances each step. Do **not** shortcut a multi-step verification into fewer calls. **The classic trap: credential changes (phone / email).** If the design opens the flow by sending a code to the **CURRENT** contact, the correct flow is **send-code-to-current → verify-current → set-new (sends new code) → verify-new (commits)** — four endpoints, not two. The base already ships the exact OTP types for this (`OtpType::OLD_PHONE_VERIFY` → `NEW_PHONE_VERIFY`; same for email) — walk the Figma flow screen-by-screen and build **each** step; never emit only the "new" half.
- **Capture the on-screen success/error copy.** For every action, record the **exact** success / toast / inline-error wording the screen shows — it becomes the API message (ar+en), not a generic "done successfully".
- Write a **Screens map** into `analysis.md` (screen → fields → **all actions** → entity/API per action). Count the screens and state the total — if it's far below what the project implies, you haven't finished.

### Reading the analysis PDF / brief (secondary source — NEVER a blind text dump)
**Prefer reading the PDF visually, page by page** (the Read tool renders pages — you SEE diagrams). **If the
tool can't render it** (poppler / `pdftoppm` not installed), fall back to a text extractor
(`pdftotext -layout file.pdf -`) — that recovers text + tables but **not** diagrams/screenshots; recover the
lost visuals from Figma (the source of truth for screens) or ask the developer to export diagram-only pages
(e.g. an ERD image), and flag anything still unread as an open `<?>`.
Extract the **meaning** of every element, not the text alone:
- **ERD / flow diagrams** → entities, columns, relationships, states.
- **Tables** (pricing, statuses, VAT, fees, limits) → exact values + enums (e.g. VAT 15%, gateway fee 2.5% + 0.5).
- **Screenshots** → fields/actions (cross-check against Figma).
- **Text** → rules, constraints, business logic, integrations.

Drop only pure **styling** (fonts/colors/layout/decorative logos). Don't *willingly* reduce the PDF to plain
text — its diagrams and tables are usually the densest, most decision-critical content; if tooling forces
text-only, recover those visuals from Figma or by asking (above) — never silently lose them.

**Source precedence:** the **Figma is the source of truth for screens/fields/flows**; the PDF **complements** it
with rules / numbers / business logic / data-model hints. On any conflict, prefer the design and record the
discrepancy as an open `<?>` for the GATE.

## 2) Review the EXISTING base schema first
- List all existing tables across every feature: read `database/schema/*.json` (+ `features.json`) and `app/Models`.
- For **every** entity and column from the design, classify it:
  - **REUSE** — already exists in base (use as-is; include as an `external: true` reference table in the new feature file).
  - **ALTER** — table exists but needs new columns (record the columns to add via a new migration; do NOT recreate the table).
  - **NEW** — does not exist; create it.
- **Do not duplicate** base tables. Typical reuse in this base: `users` + `otps` (auth/OTP/Nafath), `media` (Spatie — files/images), `pages` (legal/static), `notifications`, `contact_messages`, `settings`, `countries`/`roles`/`permissions`.
- **Verify against the ACTUAL database & migrations — not just the `*.json` files.** The `database/schema/*.json` set can be **incomplete or stale**; a table/column may already exist in the DB even if it's absent from the JSON. Before classifying anything NEW/ALTER, confirm with `Schema::hasTable('x')` / `Schema::hasColumn('x','y')` and scan `database/migrations/`. (Real example: this base's DB already had `users.national_id`/`life_status` and a `vaults` table that the schema JSON didn't show — a blind ALTER/CREATE then fails at migrate time.)
- **Before adding an ALTER column, check whether an existing column already covers the concept under a different name** — don't add a synonym. e.g. design "block account" → reuse `users.is_blocked`; "notifications toggle" → `users.is_notify`; "avatar" → `users.image`. Only ALTER/CREATE for fields/tables that genuinely don't exist yet.

## 3) Files & media — decide and represent
Every file/document/image field gets one of:
- **Spatie media** (base default): the polymorphic `media` table, with named collections (e.g. `documents`, `images`) on the owning model. No new table; record the collections.
- **Dedicated `<entity>_files` table**: only when you need per-file columns (type, size, order, original_name). Record columns + relation.
Record the choice per field — never leave files unrepresented.

## 4) Translations, enums, soft-deletes, relations
- **Translatable** fields → Astrotomic `*_translations` (mark them; the base table holds only non-translatable columns).
- **Enums** → define every value.
- **soft_deletes** → decide per table.
- **Relations** → define all FKs + `relations[]` (sync per the contract).
- **Polymorphic ownership (morph)** → when a record can belong to **more than one owner type** (e.g. wallet, favorites, cart, reviews, addresses, notifications) — especially anything ownable by different user types — model it **polymorphically**: `morphs('<owner>')` → `<owner>_type` + `<owner>_id`, with `morphTo`/`morphMany`. **Not** a single FK. This is the base's own pattern (`media`, `notifications`, `complaints`, `contact_messages`). Mark it in the schema JSON (the two morph columns + the composite index `morphs()` adds).

## 5) Emit the schema JSON
Per [`../schema-json-contract.md`](../schema-json-contract.md): write `database/schema/<feature>.json` and register the feature in `features.json`. Include REUSE tables as `external: true` reference cards so relations render on `/schema-designer`.

## 6) GATE — developer review (hard stop)
Present to the developer on `/schema-designer` **plus**: the **reuse / alter / new** map, the **files** decision, the **screens map** coverage, and any open `<?>`. **Do NOT run `build-database` until the developer approves.**

**Resume rule (mandatory):** when the developer replies "كمل"/"موافق"/"approved", **re-read `database/schema/<feature>.json` + `features.json` from disk before doing anything else.** The developer typically edits the schema on `/schema-designer` during the GATE and saves it; the on-disk file is now the source of truth. Build from that file — diff it against your pre-gate proposal, adopt every edit, and never proceed from the pre-edit version held in memory.

---

## Verify (definition of done)
- Every Figma screen enumerated and mapped (state the count); nothing derived from text alone.
- Every entity/column classified **reuse / alter / new**; zero duplicated base tables.
- Every file/image field represented (media collection or `*_files`).
- Enums valued, translatable fields marked, soft-deletes decided, relations + FKs defined.
- `database/schema/<feature>.json` loads on `/schema-designer`; reuse tables show as external.

## References (single source — do not duplicate here)
- Schema JSON contract → [`../schema-json-contract.md`](../schema-json-contract.md)
- Code generation from the approved schema → [`build-database.md`](build-database.md)
- Analysis template → [`../project-init/analysis.md`](../project-init/analysis.md)
- Identity + Figma link → `docs/project/brand-identity.md` · orchestration → [`README.md`](README.md)
