Frontend ↔ backend gaps — REQ reconciliation
Generated from the canonical Markdown — do not hand-edit. Audit date 2026-07-10.
Audit date: 2026-07-10 · Sources: dev/shared-working-context/frontend/requests/for-backend.md (REQ-001…015), the frontend phase reports/STATUS (f0–f8), the client code's mock flags, the published contracts (dev/contracts/domains/*.md + dev/contracts/openapi/swagger.v1.json), and the server code. Every verdict was checked against both the contract surface and the actual DTO/handler/controller code.
Headline: the backend chain is complete (b0–b15), but of the 15 filed REQs only REQ-001 and REQ-015 are effectively satisfied and REQ-010 is a documentation fix — the other 12 are undelivered. Every REQ still reads Status: open in the tracker. As a direct consequence, 11 of the client's 12 service domains still default to mock-primary (only auth is real-default, client/src/services/auth/constants.ts:6). Beyond the filed REQs, the unbuilt frontend phases f9–f15 will consume backend surfaces that mostly exist — with one data gap (catalog option groups) and one pre-flagged shape gap (checkout VAT line).
Verdict summary
| REQ | Ask (short) | Verdict | One-line evidence |
|---|---|---|---|
| REQ-001 | Envelope / casing / pagination shape | Done (confirm + caveat) | ApiResult + PagedResult match the typed shape; statusCode is an integer enum |
| REQ-002 | codeLength/expiresInSeconds on RequestOtpResult | Missing | RequestOtpResult.cs:7 has only OtpSent, ResendAvailableInSeconds |
| REQ-003 | Machine error codes for verify_otp | Missing | envelope has no code slot; lockout differs only by message text |
| REQ-004 | activeRole on MeResult (confirmation) | Missing (answer: client owns it) | MeResult.cs:9 — no ActiveRole anywhere in the contract |
| REQ-005 | Patient relation + conditions | Missing | PatientDto.cs:7, create/update commands unchanged |
| REQ-006 | Avatar upload route + avatarUrl | Missing | zero IFormFile/avatar matches in server/src |
| REQ-007 | Customer name + preferred language | Missing | upsert body is emergency-contact only |
| REQ-008 | Accept client map pin on address create/update | Missing | commands have no lat/lng; always geocodes |
| REQ-009 | provinceId on CustomerAddressDto | Missing | DTO ends at RecipientPhone |
| REQ-010 | pageSize vs page_size | Partial | server binds pageSize (verified); contract docs still say page_size |
| REQ-011 | Nurse credential_details endpoint + isRequired | Missing | no such route/command; step DTO lacks isRequired |
| REQ-012 | Search row name/avatar/distance + GET nurses/{id}/profile | Missing | DTO ids-only; no profile action on NursesController |
| REQ-013 | variantPrice on BookingRequestDto | Missing | DTO has unit without price |
| REQ-014 | variantLabel/patientAge on list item | Missing | list DTO omits both |
| REQ-015 | Status enum codes + checkInAddressMatch tri-state (confirmation) | Done (verified in code) | snake_case string constants on the wire; null-when-no-GPS confirmed |
Per-REQ detail
REQ-001 — envelope, casing, pagination — Done, needs a written confirmation + one caveat
- Frontend expects: payload always under
datain{ isSuccess, statusCode, message, requestId, data }; camelCase JSON; lists as{ items, total, page, pageSize }. - Backend ships: exactly that.
ApiResultatserver/src/Core/Baya.Application/Models/ApiResult/ApiResult.cs:8(+ genericDataat:28);PagedResult<T>(Items, Total, Page, PageSize)atserver/src/Core/Baya.Application/Models/Common/PagedResult.cs:4; camelCase is the System.Text.Json default (no naming-policy override exists inserver/src/API); swagger confirms (dev/contracts/openapi/swagger.v1.json:13448envelope,:14205paged shape). - Caveat to communicate:
statusCodeserializes as an integer (ApiResultStatusCodeenum, swagger:13468-13469) — matches the client'snumbertyping, but worth stating so nobody expects an HTTP-status string. - Fix: zero code. Write the confirmation into the REQ and mark it delivered.
REQ-002 — OTP length + expiry — Missing
- Expected:
RequestOtpResult { otpSent, resendAvailableInSeconds, codeLength, expiresInSeconds }. - Actual:
server/src/Core/Baya.Application/Models/Identity/RequestOtpResult.cs:7— two fields only; handler returns them atFeatures/Identity/Commands/RequestOtp/RequestOtpCommand.Handler.cs:67; swagger agrees (swagger.v1.json:16539). The client hardcodesOTP_CODE_LENGTH = 6(client/src/services/auth/constants.ts:19). - Fix: add the two ints (code length is a constant today; TTL from the OTP provider options). S effort.
REQ-003 — machine-readable verify_otp errors — Missing
- Expected: stable
code(otp_invalid|otp_expired|otp_locked) +retryAfterSecondson lockout. - Actual: the envelope has no
codeslot (ApiResult.cs:8;OperationResultcarries only boolean flags —Models/Common/OperationResult.cs:14-31). Wrong/expired share one anti-enumeration message (Features/Identity/Commands/VerifyOtp/VerifyOtpCommand.Handler.cs:24,30,34,50); lockout is a different English string only (:38). The client keys off the mock-onlyotp_lockedcode (client/src/services/auth/constants.ts:29). - Fix: add an optional
code(+ optionaldata) to the failure envelope — a smallOperationResult/ApiResultextension — and emitotp_locked+retryAfterSecondsfrom the lockout branch; keep wrong-vs-expired collapsed if enumeration-safety is preferred (state that in the REQ answer). S–M effort (the only REQ touching a cross-cutting type).
REQ-004 — activeRole confirmation — Missing (recommend: answer "client owns it")
- Actual: no
activeRoleonMeResult(server/src/Core/Baya.Application/Models/Identity/MeResult.cs:9) or anywhere in the contract (schema scan). No endpoint persists a current-role choice. - Fix: zero code — write the decision (client-owned
intended_rolestands) into the REQ so the router behavior is contract-blessed. If the product later wants a persisted active role, it's ame/select_roleextension.
REQ-005 — patient relation + conditions — Missing
- Actual:
PatientDtoends atInitialMedicalNotes/IsActive(Models/Identity/PatientDto.cs:7,15-16); create/update commands unchanged (Features/Identity/Commands/CreatePatient/CreatePatientCommand.cs:12-19,UpdatePatient/UpdatePatientCommand.cs:8-16); norelation/conditionsin any schema. - Fix:
relationas a nullable code column;conditionsas stable codes (JSON column or child table — child table if search/filtering is ever wanted). Gate: flipsUSE_PATIENTS_MOCK(client/src/services/patients/constants.ts:8). S–M effort.
REQ-006 — avatar upload + avatarUrl — Missing
- Actual: zero
IFormFile/multipart/avatar usage inserver/src(repo-wide grep); noavatarUrlonNurseProfileDto(swagger:18945) orCustomerProfileDto(Models/Identity/CustomerProfileDto.cs:7). The client's real path deliberately throws 501 (client/src/services/profiles/apis/clientApi.ts:84). - Fix:
POST api/v1/{nurse|customer}_profiles/avatar(multipart, size/type-validated) storing viaIObjectStorage+avatar_urlcolumn on both profiles. Note it also feeds REQ-012 (search card avatar) and REQ-013 (nurse avatar on request detail) — deliver before or with those. M effort (first multipart endpoint; pairs with the object-storage swap, plan §5.5).
REQ-007 — customer name + preferred language — Missing
- Actual: upsert body is emergency-contact only (
Features/Identity/Commands/UpsertCustomerProfile/UpsertCustomerProfileCommand.cs:11);MeResultexposes name read-only (MeResult.cs:12); nopreferredLanguageanywhere (schema scan). - Fix: decide the home (recommend: extend the upsert to write
Users.FirstName/LastName+preferred_languageon the customer profile) and answer the REQ. S effort.
REQ-008 — accept the client map pin — Missing
- Actual: create/update commands have no coordinates; the server always geocodes (
Features/Addresses/Commands/CreateAddress/CreateAddressCommand.cs:9-12,UpdateAddress/UpdateAddressCommand.cs:8-9; swagger:18051). The user's pin is silently discarded on the real path — exactly what the REQ warned. This also degrades EVV accuracy (b9 measures distance to the stored coordinate; a mock/geocoded centroid is ±5 km off —CrossCutting/Seams/MockGeocoder.cs:52). - Fix: optional
latitude/longitudeon both bodies; when present store as sourceuser_pin, else geocode as today. S effort; do before the real geocoder swap (plan §5.4).
REQ-009 — provinceId on CustomerAddressDto — Missing
- Actual: DTO fields run
Id..RecipientPhone(Models/Addresses/CustomerAddressDto.cs:9-24); noprovinceId(swagger:17983— the property exists only onCityDto). - Fix: join
cities.province_idinto the address projections. S effort. Gate (with REQ-008):USE_ADDRESSES_MOCK(client/src/services/addresses/constants.ts:10).
REQ-010 — pageSize param name — Partial (server verified; docs stale)
- Actual: every list binds a
PageSizerecord property via[FromQuery]— so the working wire name is camelCasepageSize(case-insensitive), andpage_sizesilently does not bind. Verified:Features/ServiceAreas/Queries/ListMyServiceAreas/ListMyServiceAreasQuery.cs:8,Features/Variants/Queries/ListMyVariants/ListMyVariantsQuery.cs:8,Controllers/V1/NurseServiceAreasController.cs:35,AdminPayoutsController.cs:54; swagger names the parameterpageSize(swagger.v1.json:3104). But the requested deliverable — fixing the docs — never happened:dev/contracts/domains/catalog.md:41andconfig-reference.md:11(and others, e.g. bookings-evv.md, verification.md) still writepage_size. - Fix: sweep the contract docs to
pageSize, answer the REQ. Zero server code.
REQ-011 — nurse credential_details + isRequired — Missing
- Actual: the nurse-facing controller exposes only submit/status/upload_url/documents/run (
Controllers/V1/NurseVerificationController.cs:26-57); repo-wide grep forcredential_details|SubmitCredentialfinds nothing.VerificationStepDtohas noIsRequired(Models/Verification/VerificationDtos.cs:17); the flag exists only on the admin step-type catalog. Consequence on the real path: the INO number + specialties a nurse types are silently dropped (verificationClientApi.submitCredentialDetailsno-ops —dev/shared-working-context/reports/frontend-phase-5-report.md:101). - Fix:
POST api/v1/nurse_verification/credential_detailswriting the structurednurse_credentialsfields (the registry table already stores number/authority/expiry), + projectisRequiredonto the step DTO. M effort. Gate:USE_VERIFICATION_MOCK(client/src/services/verification/constants.ts:9).
REQ-012 — search enrichment + public nurse profile — Missing (highest-leverage gap)
- Actual:
NurseSearchResultDtocarries ids + price/rating/gender/geo only (Models/Search/NurseSearchResultDto.cs:8-19); the publicNursesControllerhas trust_badge, reviews, review_tags — no/profile(Controllers/V1/NursesController.cs:24-36); noavatarUrl/distanceKmanywhere in the contract (schema scan). - Why it leads the priority list: C2/C3 are the trust funnel — the family picks a named, faced, priced nurse here; this single REQ keeps
services/searchmock-primary (client/src/services/search/constants.ts:9) and blocks the whole discovery→request→booking real-path chain (search feeds C4's nurse/variant ids). - Fix: (a) denormalize
nurse_name/avatar_urlintonurse_search_index(the maintainer already re-derives rows from source —Persistence/Services/Search/SearchIndexMaintainer.cs:25; add columns + reindex-on-profile-change) or join at query time inSqlNurseSearch;distanceKmis optional — the district model makes it derived-if-cheap. (b) an aggregatedGET nurses/{id}/profilecomposing existing reads (profile + variants + trust badge + latest published review). M effort; depends on REQ-006 for the avatar itself.
REQ-013 — variantPrice on BookingRequestDto — Missing
- Actual: the DTO has
VariantLabel+VariantPriceUnitbut no price and no nurse avatar (Models/Booking/BookingRequestDto.cs:20-21, full list:11-42; swagger:16674). - Fix: join the variant's
Price(IRR digit-string, consistent with the money convention) into the projection. The money-free rule stays intact — this is the display rate, not an engagement total (the request row still stores no money). S effort.
REQ-014 — variantLabel/patientAge on the inbox row — Missing
- Actual:
BookingRequestListItemDtohas neither (Models/Booking/BookingRequestListItemDto.cs:10-21; swagger:16903) — the nurse inbox can't show which service was requested without opening the detail. - Fix: add
variantLabel(already on the detail DTO);patientAgeas a coarse band if product wants it. S effort. Gate (with REQ-013):USE_BOOKING_REQUESTS_MOCK(client/src/services/bookingRequests/constants.ts:14) — though that flip also needs the upstream domains real (see below).
REQ-015 — enum codes + checkInAddressMatch tri-state — Done (verified), needs a written confirmation
- Verified in code: statuses are stored/projected as snake_case string constants — exactly the client unions:
Domain/Entities/Booking/BookingStatus.cs:11-30,BookingSessionStatus.cs:10-19,VisitVerificationStatus.cs:11-17; DTOs copy them verbatim (Models/Booking/BookingDtos.cs:97), so no PascalCase/int ever hits the wire.checkInAddressMatchisbool?(BookingDtos.cs:104) assigned only inside the lat/lng-present branch (Features/Bookings/Commands/CheckInVisit/CheckInVisitCommand.Handler.cs:64-77) → null when GPS is absent; afalseis advisory only (support alert + notification, no block —:90-109). - One nuance to include in the answer:
nullalso occurs when GPS was sent but the frozen booking address has no resolvable coordinates — the client copy for «موقعیت ثبت نشد» should tolerate that. - Fix: zero code; write the confirmation, mark delivered.
Beyond the filed REQs — what f9–f15 will hit
Frontend phases f0–f8 are built (reports exist); f9–f15 are specs only. Reconciling their declared consumption against the shipped backend:
| Upcoming phase | Consumes | Backend reality | Verdict |
|---|---|---|---|
| f9 checkout/card | b10 payments.md + b11 invoice | endpoints exist (initiate/webhook/GET invoices/{booking_id}), but no checkout-summary read with the VAT line — f8 already flagged BookingDetailDto has no tax field (reports/frontend-phase-8-report.md:113); f9's spec expects vat_irr/vat_rate/redirect_url shapes (dev/phases/frontend/frontend-phase-9-b10.md:117,330) | Partial — pre-file the checkout-summary REQ now |
| f10 refund status | b11 refunds-invoices.md | GET refunds/{id}/status + GET invoices/{booking_id} shipped (Controllers/V1/RefundsController, InvoicesController) | Done (verify shapes when f10 runs) |
| f11 BNPL | b12 bnpl.md | full eligibility→initiate→status surface shipped (CheckoutBnplController) | Done (verify shapes) |
| f12 nurse earnings | b13 payouts.md | nurse_payouts/history + admin console shipped (NursePayoutsController, AdminPayoutsController) | Done (verify shapes) |
| f13 reviews/care records | b14 reviews-records.md | submit/list/tags/moderation + care records shipped (5 controllers) | Done (verify shapes) |
| f14 tickets + notifications | b15 + b1 notifications | tickets shipped; notifications verified present: GET notifications/get_notifications/get_unread_count, POST mark_notification_read/mark_all_read (Controllers/V1/NotificationsController.cs:24-42) — the f14 spec's worry about missing b1 endpoints is unfounded | Done |
| f15 admin/partner consoles | admin endpoints across b1/b6/b11/b13/b14/b15 | all shipped per the chain (verification queue, refunds, payouts, moderation, config/holidays/audit/support-alerts, partner centers) | Done (expect shape-polish REQs when f15 runs) |
Data gap (not a contract gap): flipping USE_CATALOG_MOCK against a fresh backend yields categories with no option groups — only the 5 categories are seeded; groups/values are admin-authored and the admin catalog UI is f15 (reports/frontend-phase-4-report.md:92). Until f15 (or a seed migration), the variant builder's required-option step has nothing to render on the real path. Recommend: a small representative option-group seed, or prioritize the f15 catalog manager.
Tracker hygiene: all 15 REQs read Status: open (for-backend.md:32…216) and the mocks-registry's early block contradicts its own later rows (see plan §7.6). Whoever lands this batch should update both in the same change.
Frontend-unblock priority
- REQ-012 (search row enrichment + public profile) — unlocks the discovery funnel; everything downstream needs C2/C3 real. Include the
nurse_search_indexcolumns + reindex. - REQ-005, REQ-008, REQ-009 — the booking-request inputs (patients, addresses) go real; REQ-008 also protects EVV accuracy.
- REQ-013, REQ-014 — the request flow prices/labels itself; with (1)+(2) the whole search→request→accept chain can flip to real.
- REQ-006, REQ-007 — profile/avatar polish; REQ-006 also feeds (1) and (3)'s avatar fields.
- REQ-011 — verification detail capture (stops silent INO/specialty data loss).
- REQ-002, REQ-003 — auth UX polish (real path already works without them).
- Zero-code batch: REQ-001, REQ-004, REQ-010, REQ-015 — written confirmations + contract-doc
page_sizesweep + tracker statuses. - Pre-file the f9 checkout-summary REQ (VAT line, redirect_url, idempotency header echo) so b-side work can precede the f9 build.