# Refinement phases — making Balinyaar work as one integrated app **Created:** 2026-07-10 · **Scope:** the whole repo (client + server) after the 16+16 phase chain completed · **Method:** a read-only audit of the live code (three parallel exploration passes over the frontend wiring, the backend runnability/infra, and the frontend↔backend contract), cross-checked against `dev/` docs. This directory is a **runnable chain of 10 refinement phases** that takes the repo from *"both projects build and each runs on its own, but they aren't actually integrated"* to *"one working app where the logic takes effect."* Run them **in order, one at a time**, pointing a fresh agent at one phase file (*"Execute `dev/post-phase/refinement/refinement-phase-0-bring-up.md` end to end"*). > **Why this exists alongside [`../server/`](../server/README.md).** The existing > [server post-phase audit](../server/post-phase-backend-plan.md) is excellent — but it audits the **backend in > isolation**. It never covers the thing you actually hit when you ran the app: the two projects were built in > **parallel, decoupled lanes and never run against each other**. This chain adds the missing **integration + > frontend track** (phases 0–4) and folds the server audit's 8 buckets into the back half (phases 5–9) in the > right order. --- ## Two things that are NOT wrong (correcting the brief) You suspected these; the audit found they're actually fine — which is good news: 1. **"Database migrations are behind" — they're not.** There are **17 EF migrations** tracking every backend phase b0→b15; the model snapshot is in sync and the working tree is clean. `Program.cs` applies them on every boot. If it *felt* like migrations were behind, it's because your **target database was never migrated** (you were running the frontend on mocks, so nothing ever touched a real DB) — not because migration files are missing. The real DB gap is **no local-dev database story + no demo seed data** (Phases 0–1), not drift. 2. **"Missing services like Redis / Elasticsearch" — not needed for the MVP.** The **only external service the backend requires today is SQL Server.** All 18 vendor/infra dependencies (cache, lock, jobs, storage, SMS, payments, KYC, geocoding, search, moderation) are **in-process mocks behind DI seams — by design.** Redis becomes necessary only when you run a **second API instance** (Phase 7); Elasticsearch is **never MVP** (the SQL search is real and correct). You didn't miss configuring them. --- ## What's actually wrong (the problem inventory) Every item below is verified in the live code (file/line evidence is in the phase files). ### A. The frontend barely talks to the backend - **21 of 22 client service domains default to an in-browser mock** (`USE_*_MOCK = true`, hard-coded literals in each `services/{domain}/constants.ts`). **Only `auth` is real by default.** So a running app shows fake in-memory data everywhere except login. → **Phase 4** (flip them, in dependency order). - **The backend has no CORS at all** (zero `AddCors`/`UseCors` in `server/`). Even the calls the frontend *does* make would be blocked by the browser once the two run on different origins. **This is the #1 hard blocker.** → **Phase 0**. - The client is correct and ready (`clientFetch` reads `NEXT_PUBLIC_API_URL`, unwraps the `ApiResult` envelope, does silent 401 refresh); each domain's real `clientApi` already maps the live routes 1:1. The swap is a **one-line flag flip per domain** — *once the backend serves what that domain needs* (see C). ### B. "No auth / only the customer side shows" - Auth **is** real and works — but a fresh phone login holds only the `customer` role, so `resolveRoleDestination` sends everyone to the customer app (which lives at the root `/`). Nurse/admin shells are never auto-reached. - A hard-coded `DEFAULT_ROLE = customer` fallback means **un-hydrated or failed `/me` role state silently renders the customer shell** — a nurse can be shown the customer app. There are no client-side role guards. - OTP delivery is a **log statement** (`LoggingSmsSender`), so "logging in" requires reading the code from the server console — fine for dev, but there's no real SMS yet. → **Phase 2** (role reachability + robust hydration + guards) and **Phase 0/8** (the dev OTP bridge, then real SMS). ### C. Frontend ↔ backend contract mismatches (the real "mismatch" you sensed) - The frontend filed **37 contract requests (REQ-001…037) — all still `Status: open`.** They're why 21 domains are mock-primary: a DTO missing a field, or a customer read that only exists as an admin route. Almost all are **additive** (nullable fields, new read endpoints). → **Phase 3**. - Three concrete shape mismatches to reconcile before flipping the matching mock: partner-center **kebab-case** routes that violate the snake_case convention; the BNPL **`balinyaar`** provider not in the wire enum; the **client-invented cancellation-policy codes**. → **Phase 3**, verified in **Phase 4**. ### D. It can't start cleanly / shows nothing - The backend only boots against a **committed remote SQL Server** (leaked `sa` credentials); no local default, no SQLite fallback for `dotnet run`. → **Phase 0/1** (local DB) and **Phase 5** (rotate the leak). - A fresh DB seeds only lookup tables + one admin + one gateway — **no nurses, variants, or search rows** — so search/discovery/booking are empty even on the real path. → **Phase 1** (demo seed). ### E. Production-readiness gaps (from the server audit — real, but after "make it work") - Committed secrets & dev-grade defaults (Phase 5); one genuine money hole — the **unreachable BNPL/manual refund clearing** (Phase 6); **no unattended operation** — nurses aren't paid unless an admin clicks (Phase 7); every vendor rail is mocked — **SMS is launch-critical** (Phase 8); metrics-only observability + doc drift (Phase 9). --- ## The 10 refinement phases Run top to bottom. Phases 0–4 make the app **work as an integrated whole**; phases 5–9 make it **production-ready** (and largely re-sequence the [server audit](../server/post-phase-backend-plan.md)). | # | Phase | Track | Fixes (from above) | Depends on | | --- | --- | --- | --- | --- | | **0** | [Local end-to-end bring-up & the integration seam](refinement-phase-0-bring-up.md) | both | CORS · local DB · dev OTP · one real round-trip | — | | **1** | [Database: local-dev story, demo seed & migration hygiene](refinement-phase-1-database-and-seed.md) | backend | empty marketplace · local DB · (migration myth) | 0 | | **2** | [Auth & role-aware navigation](refinement-phase-2-auth-and-role-nav.md) | frontend (+bit of backend) | "only customer side shows" | 0, 1 | | **3** | [Backend contract batch (close the 37 REQ gaps)](refinement-phase-3-contract-batch.md) | backend | the mismatch — REQ-001…037 | 0 | | **4** | [Frontend de-mock (flip every domain to real)](refinement-phase-4-frontend-de-mock.md) | frontend | "no API calls to backend" | 1, 2, 3 | | **5** | [Security & config hygiene](refinement-phase-5-security-hygiene.md) | backend | leaked `sa` creds, keys, seeds | — (5.1 now) | | **6** | [Money-path correctness completion](refinement-phase-6-money-correctness.md) | backend | unreachable refund clearing + FKs | before 8 | | **7** | [Unattended ops: scheduler, locking & multi-instance](refinement-phase-7-unattended-ops.md) | backend | nurses not paid unattended · Redis gate | 6 | | **8** | [External rails go real (SMS → trust → money)](refinement-phase-8-external-rails.md) | backend | mocks → real vendors (SMS launch-critical) | 6, 7 | | **9** | [Observability, ops hardening & scale-later](refinement-phase-9-observability-and-scale.md) | backend | tracing/health/logs · doc honesty · ES/analytics deferred | — | ### Dependency & sequencing ``` Make it work (integration): 0 bring-up ──► 1 database/seed ──► 2 auth & role nav ─┐ └────────► 3 contract batch ────────────────────┴──► 4 frontend de-mock (app now integrated) Make it production-ready (server audit, re-sequenced): 5 security (5.1 rotate creds = TODAY) 6 money correctness ──► 8 money rails 7 scheduler/Redis ─────► (weekly payout trigger, Moadian poll for 8) 8 external rails (5.1 SMS = launch-critical, do first in the phase) 9 observability & scale-later (start anytime, finish before launch) ``` **Two items that jump their slot:** **Phase 5 §5.1** (rotate the committed `sa` credentials — the leak is live *today*) and **Phase 8 §5.1** (real SMS — no real user can log in without it, sequence it first inside Phase 8). **Minimum path to "a working demo on real data":** Phases **0 → 1 → 2 → 3 → 4**. After those, the whole customer/nurse/admin funnel runs against the real backend with seeded data (payments still use the dev conversion path until Phase 8 swaps a real PSP). --- ## How the phase files are written Each file follows the repo's [phase template](../../phases/_shared/phase-template.md): a one-paragraph mission, context (what already exists — don't rebuild), required reading, enumerated scope, the seams/mocks it touches, the invariants it must not break, a Definition of Done, concrete how-to-test steps, and a close-out (docs + memory). Phases 0–4 are written in full; phases 5–9 are concise and **point into the already-detailed [server audit](../server/post-phase-backend-plan.md)** (which has file/line evidence for every item) rather than restating it. ## Related documents - [../server/post-phase-backend-plan.md](../server/post-phase-backend-plan.md) — the backend-only audit's 8 buckets (folded into Phases 5–9), with file/line evidence. - [../server/frontend-backend-gaps.md](../server/frontend-backend-gaps.md) — REQ-001…015 reconciled against shipped code (Phase 3 extends this through REQ-037). - [../server/runtime-services.md](../server/runtime-services.md) — the 17-service deployment topology. - [../../shared-working-context/frontend/requests/for-backend.md](../../shared-working-context/frontend/requests/for-backend.md) — the canonical REQ-001…037 tracker (Phase 3's spec). - [../../shared-working-context/reports/mocks-registry.md](../../shared-working-context/reports/mocks-registry.md) — every seam + its make-it-real steps (backend seams + frontend mock flags), the checklist for Phases 4 & 8.