Files
baya-monorepo/dev/shared-working-context/reports/refinement-phase-1-report.md
T
2026-07-13 00:49:24 +03:30

7.3 KiB
Raw Blame History

Refinement Phase 1 — Database: local-dev story, demo seed & migration hygiene — Report (2026-07-13)

What was built

Turned a freshly-migrated database from an empty shell (reference/lookup rows + one admin + one gateway) into a populated demo marketplace, so every real-path discovery/search/booking screen has data. No new migration, no new endpoint, no contract change, and the reference HasData seeds are untouched — the demo seeder writes alongside them through the existing entities and the real search maintainer.

  • Development-gated demo seeder (§3.2). New Persistence/Services/Seeding/DemoWorldSeeder.cs (scoped, registered in AddPersistenceServices) + DemoWorldDefinitions.cs (the static, deterministic persona data). Invoked from Program.cs only under app.Environment.IsDevelopment(), after SeedDefaultUsersAsync + SeedPaymentGatewaysAsync, via the new SeedDemoWorldAsync() extension. It creates:
    • 3 nurses (phone users + nurse role via IAppUserManager, each with a nurse_profiles row): two fully verified (is_verified flipped through the guarded MarkVerified(), a nurse_verifications row approved, credentials, a primary bank account with matched_national_id=1), one unverified (pending, no credentials/bank). Each verified nurse gets 23 nurse_service_variants across price units (IRR BIGINT) + nurse_service_areas in Tehran (one whole-city district_id=NULL, some specific districts).
    • 2 customers (phone users + customer role) each with a customer_profiles row, 12 patients (with gender for same-gender matching + encrypted initial_medical_notes), and a customer_addresses row with coordinates.
    • One cross-category, required option group — شیفت / Shift Type (Daytime/Night/Live-in) — so the variant builder's required-option step renders on the real path in Development (the "categories but no option groups" data gap). Each variant answers it; the OptionSetHash is computed the same way the real CreateVariant handler does.
    • The search index is driven through the real ISearchIndexMaintainer.RebuildAsync (never hand-inserted) — the single place is_searchable is computed, so the seeded world is identical to one built by real usage.
  • Idempotency (§5). Every persona is guarded on its phone number (GetUserByPhoneNumber); the option group on its name. Re-running the seeder on an already-seeded DB is a no-op (it still re-derives the search index, which is itself idempotent).
  • Local DB story finished (§3.1). Phase 0 already delivered docker-compose.yml (SQL Server 2022 on 1433), placeholder appsettings*.json connection strings, the UserSecretsId, and boot-time MigrateAsync. This phase verified those and documented the explicit dotnet ef database update path (for the migrations-off-boot / deploy direction Phase 7 formalises) plus the demo-seed reset flow in the RUNBOOK.
  • Migration hygiene (§3.3). Confirmed the 17 migrations are current and there are no pending model changes (the demo seeder adds runtime data, not schema — it changed no entity config and no migration). The three promised-but-missing forward-dep FKs and the boot-migrate multi-instance race are explicitly out of scope (Phase 6 / Phase 7 respectively), as the phase directs.

The exact demo world (for the frontend de-mock phase to log in as)

Phone Role Who Gender State
09120000001 nurse زهرا عزیزی female verified, accepting · variants: Elderly per-24h 3500000, Elderly per-hour 250000, Post-Surgery per-day 2000000 · areas: whole-city Tehran + منطقه ۱ + منطقه ۳
09120000002 nurse علی کریمی male verified, accepting · variants: Chronic per-day 1800000, Post-Surgery per-24h 3200000 · areas: منطقه ۳/۶/۱۲
09120000003 nurse مریم احمدی female unverified (pending) · Infant per-session 800000 · منطقه ۲ — never in search
09120000010 customer سارا محمدی female patients: حسن (male), فاطمه (female) · address in منطقه ۳ (coords)
09120000011 customer رضا حسینی male patient: امیرعلی (male, infant) · address in منطقه ۶ (coords)

Tehran city_id = 101; districts are 1000+n. Category ids: Elderly 1, Post-Surgery 2, Infant 3, Chronic 4.

What is now testable (and exactly how)

  • Automated (in the suite, +3 tests → 372 total): Baya.Test.Api/DemoWorldSeederTests runs the seeder through real DI over the SQLite harness and asserts, over the real HTTP pipeline:
    1. GET /api/v1/search/nurses?service_category_id=1&city_id=101 (Elderly, verified nurses) → total > 0; service_category_id=3 (Infant, only the unverified nurse) → total == 0.
    2. The verified nurse's trust_badgeisVerified=true with non-empty credentialTypes; the unverified nurse's → false.
    3. Running the seeder twice leaves exactly Nurses.Length / Customers.Length rows (idempotent).
  • Manual (the §7 proof, requires a reachable SQL Server): wipe + dotnet run (Development) → the console logs Demo world seeded: 3 nurse(s), 2 customer(s)…; hit the two search routes above and the trust-badge routes in Swagger; re-run → Demo world already seeded — no-op. (Not runnable in this environment: the SQL Server on localhost:1433 here rejects the dev sa credential, so the automated HTTP-level tests above are the reproducible proof.)

What is mocked / waiting on a real service

  • None introduced. The seeder goes through the real handlers/maintainer where an invariant is at stake (verification flip, search reindex, OptionSetHash, encrypted-PII converters, iban_hash), so no seam and no mocks-registry.md entry. The bank account's matched_national_id is set directly to model a completed استعلام شبا inquiry (the mock IBankAccountOwnershipVerifier isn't invoked from the seeder).

Contracts

  • None produced / changed. No new route or shape; swagger.v1.json not regenerated.

Docs updated

  • server/CLAUDE.md — Persistence section notes the Development demo seeder and what it creates; Startup wiring notes the Development-only SeedDemoWorldAsync().
  • dev/post-phase/refinement/RUNBOOK.md — added the demo-seed description, the demo-account table, the explicit dotnet ef database update path, and the reset/re-seed flow.
  • Handoff: backend/handoff/after-refinement-phase-1.md.

Follow-ups for later phases

  • Phase 4 — flip the client USE_*_MOCK flags; log in as the demo accounts above and de-mock home/search/ booking against this real data.
  • Phase 6 — the additive forward-dep FKs (refunds.ticket_id, nurse_clawbacks.*_payout_id, invoices.partner_center_id) + money-correctness; not in this phase.
  • Phase 7 — split boot-time MigrateAsync out for the multi-instance / least-privilege deploy path.
  • Optional later: a couple of confirmed bookings + sessions for the demo pairs (ids 50015005) so the post-payment screens demo without running the funnel — deliberately deferred (the cleaner demo is to create bookings by running the real funnel once Phase 4 lands).