Files
baya-monorepo/dev/phases/frontend/frontend-phase-5-b6.md
T
2026-06-28 21:59:59 +03:30

340 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Frontend Phase 5 — Nurse verification flow (mocked vendors)
> **Mission:** build the trust engine's front end — the staged, platform-owned verification flow a nurse
> walks through before any of their services can go live. A nurse lands on a **status checklist** (B3),
> submits **identity** (B4: national-ID + card image + liveness selfie), submits **professional
> credentials** (B5: نظام پرستاری number + license + education + specialties), and waits on the
> **under-review** screen (B6) until an admin decides. Documents upload to the object-storage-backed
> endpoint with type/size validation and progress; each step shows its own status (and its rejection
> reason); and a **trust badge** (verified / unverified / expired) renders on the nurse profile. Verified
> trust is the entire brand — a nurse is **not bookable and cannot publish until verified**, and the UI
> must say so honestly and never advertise a check the platform doesn't actually perform.
>
> **Track:** frontend · **Depends on:** [`frontend-phase-4-b5.md`](./frontend-phase-4-b5.md) (catalog browse + nurse service builder) and the **b6** verification contract · **Unlocks:** a bookable verified nurse + the search **trust badge** consumed in [`frontend-phase-6-b7.md`](./frontend-phase-6-b7.md)
> **Before you start, read [`../_shared/agent-operating-rules.md`](../_shared/agent-operating-rules.md).** It is not optional.
---
## 1. Context — where this sits
This is the fifth feature slice of the customer/nurse front end and the **nurse side's gating step**.
By f4 a nurse can build service variants, but those variants are **inert** — they cannot surface in
search or be booked until `nurse_profiles.is_verified` is true. This phase builds the screens that flip
that switch: the data-driven verification pipeline (six step types, all vendor calls **mocked** server-side
behind DI seams in b6) rendered as a nurse-facing checklist, the two submission forms, the waiting state,
the document uploader, and the public trust badge. When this lands, a nurse can go from "registered" to
"verified and publishable", which is the prerequisite for everything downstream — search (f6), booking
(f7), and ultimately payout (f12, gated on the bank-account verification step).
**What already exists (do not rebuild) — built by prior phases:**
- **f0 foundations** ([`frontend-phase-0.md`](./frontend-phase-0.md)): the three actor app shells +
route groups, the **nurse shell** ("نمای پرستار"), the `services/{domain}` + TanStack Query caching
pattern (`keys.ts` factory, `apis/clientApi.ts`, one-hook-per-file), the contracts→`types.ts` pattern,
the money/format utils, and the **shared composite components** — most importantly the **stepper/
progress header** and the **status chip** (verified/pending/…). **Reuse both here; do not re-implement
a stepper or a status chip.** The `verification` i18n namespace was reserved in f0 — fill it.
- **f1-b2 auth** ([`frontend-phase-1-b2.md`](./frontend-phase-1-b2.md)): phone-OTP login, the role router,
roles in `AuthContext`. The nurse arrives here already authenticated with the `nurse` role. **Step 1 of
the checklist (شماره موبایل — mobile verified) is already satisfied at login** — render it as `passed`,
don't ask for it again.
- **f2-b3 onboarding** ([`frontend-phase-2-b3.md`](./frontend-phase-2-b3.md)): the nurse profile bootstrap
(`CreateNurseProfileCommand` → an unverified `nurse_profiles` row, `is_verified=false`) and the nurse
**bank-account settings** screen (IBAN entry → ownership inquiry). The verification pipeline's
`bank_account_verification` step couples to that bank account — **link to the existing bank-account
screen for that step; do not rebuild the IBAN form here.**
- **f3-b4 addresses/geo** ([`frontend-phase-3-b4.md`](./frontend-phase-3-b4.md)): the nurse coverage-area
editor and the cascading geo dropdowns (not used here, but the nurse shell nav points to them).
- **f4-b5 catalog** ([`frontend-phase-4-b5.md`](./frontend-phase-4-b5.md)): the nurse "add a service"
builder (B7) and `services/catalog`. **The "انتشار پروفایل / go live" action that f4 stubs must be
gated on verification by this phase** — wire the blocked-until-verified state into the publish CTA.
> **Honesty constraint (load-bearing, from the product doc and GTM notes):** vetting is platform-owned and
> performed at the authoritative source. **Never word the UI to advertise a check that isn't performed.**
> The MoH/INO license and criminal-record steps are *manual admin review of an uploaded document* at
> launch — copy must say "در حال بررسی" (under review), not "تاییدشده توسط نظام پرستاری" until an admin
> actually passes it. The civil-registry/Shahkar/liveness checks are real (vendor-mocked) — those may say
> "استعلام خودکار".
## 2. Required reading (do this first)
- [`../_shared/agent-operating-rules.md`](../_shared/agent-operating-rules.md) and
[`../_shared/frontend-conventions-checklist.md`](../_shared/frontend-conventions-checklist.md) — how you
work, the gate, the contract/handoff lanes, the mock-then-swap rule (§6).
- [`../../../client/CLAUDE.md`](../../../client/CLAUDE.md) — the engineering contract (RSC/client boundary,
layouts, the `services/{domain}` shape, i18n, theme, cookies, the fetch services). Non-negotiable.
- **Invoke the `frontend-designer` skill** before any visual work — it is the design/brand contract
(palette, tokens, typography, the `App*` library, layout shells, the hard UI rules). Every screen,
chip, uploader, and badge in this phase goes through it. The wireframe's status legend is **green =
automatic/verified, amber = pending, grey = manual/next, terracotta = financial** — encode those as
token-driven chip variants.
- **Product — the source of truth for the rules:**
- [`../../../product/business/02-nurse-verification.md`](../../../product/business/02-nurse-verification.md) —
the six steps, what each verifies, why it's manual vs automated, the structured-credential-registry
rationale, continuous re-verification, and **the "never advertise a check you don't perform" rule**.
- [`../../../product/wireframes/index.html`](../../../product/wireframes/index.html) — **Section B,
screens B3B6** are the visual baseline you implement: B3 status meter "۲ از ۵" + stepped checklist
with status badges; B4 identity (کد ملی field, upload national-ID card, liveness selfie, "استعلام
خودکار از ثبت احوال" note); B5 professional credentials (شماره نظام پرستاری, license upload, education
cert shown uploaded ✓, specialty chips سالمندان/ICU/+افزودن); B6 "در حال بررسی" (2448h, mini-checklist).
Also note B7's "انتشار پروفایل" — the publish gate this phase enforces, and C2/C3's "✓ تاییدشده" badge
this phase's trust-badge component feeds.
- **The contract you consume (the authoritative server shapes):**
- [`../../contracts/domains/verification.md`](../../contracts/domains/verification.md) — written by
**backend-phase-6**. The exact request/response shapes, routes, status enums, and the per-step status
codes. **Do not guess shapes** — derive `types.ts` from this doc + the published `swagger.json`. If a
shape you need is missing, append a request to
[`../../shared-working-context/frontend/requests/for-backend.md`](../../shared-working-context/frontend/requests/for-backend.md)
and mock behind the `services/verification` seam meanwhile (operating-rules §6).
- [`../../contracts/conventions/api-conventions.md`](../../contracts/conventions/api-conventions.md) and
[`money-and-types.md`](../../contracts/conventions/money-and-types.md) — the envelope (`OperationResult`
`ApiResult`, already unwrapped by `clientFetch`), `snake_case` routes/properties, pagination, **enums
as stable string codes** (mirror them as string-literal unions; labels are i18n keys, never derived
from the code), UTC timestamps with **Shamsi display client-side** (credential issue/expiry dates).
- **Code to mirror:**
- The `services/catalog` and `services/onboarding` domains from f4/f2 — the exact `types.ts`/`keys.ts`/
`apis/clientApi.ts`/`hooks/use*.ts`/`index.ts` layout your `services/verification` copies.
- The shared **stepper/progress header** and **status chip** from f0 (`src/components/…`) — extend, don't
fork. The nurse-profile bank-account screen from f2 — you deep-link into it for the bank step.
- The existing document/image handling, if any, in `App*` (`AppImage`); the `clientFetch` multipart path.
## 3. Scope — build this
A new **`services/verification`** domain, the **nurse verification route subtree** under the nurse shell,
a reusable **document-upload** component, a **trust-badge** component, and the publish-gate wiring. Build
RTL-first, both locales, query-cached, minimal re-renders.
### 3.1 `services/verification` domain (the data layer)
Copy the f0/f4 service shape exactly. Types come from
[`../../contracts/domains/verification.md`](../../contracts/domains/verification.md) — do not invent.
- **`types.ts`** — string-literal unions mirroring the contract enums: the **aggregate status**
(`not_started` | `pending` | `in_review` | `approved` | `rejected` | `suspended`), the **per-step
status** (`not_started` | `pending` | `in_review` | `passed` | `rejected`), the **step `code`s**
(`identity_kyc` · `shahkar_match` · `moh_competency_license` · `ino_membership` · `criminal_record` ·
`bank_account_verification`), `verification_method` (`manual` | `portal` | `api`), and the
badge state (`verified` | `unverified` | `expired`). Plus the DTOs the contract returns:
`VerificationStatus` (aggregate + ordered `steps[]` each with `code`, `status`, `is_automated`,
`rejection_reason?`, `expires_at?`), `VerificationStep`, `VerificationDocument` (storage key, file name,
uploaded-at — **metadata only, never bytes**), `NurseCredential` (type, masked number, holder name,
issuing authority, `issued_at`/`expires_at`).
- **`keys.ts`** — a query-key factory: `verificationKeys.status()`, `.documents(stepCode)`,
`.badge(nurseId)`. Deliberate `staleTime` (status is moderately fresh — e.g. 30s — because submitting a
step changes it; the badge is longer-lived).
- **`apis/clientApi.ts`** wrapping `clientFetch` — one function per contract endpoint (see §3.2). The
document upload uses the multipart path against the `IObjectStorage`-backed endpoint.
- **`hooks/` — one hook per file:**
`useVerificationStatus` (query), `useStartVerification`, `useSubmitIdentity`, `useSubmitCredentials`
(or per-credential submit if the contract splits MoH/INO/criminal), `useUploadVerificationDocument`
(mutation with progress), `useNurseTrustBadge` (query). **Every mutation invalidates
`verificationKeys.status()`** (and the badge where relevant) so the checklist re-renders from cache
without a manual refetch. Don't toast 401/403/5xx (the fetch layer does) — only domain 4xx (e.g.
"ownership mismatch", "shared-SIM", "national-ID format").
### 3.2 Endpoints consumed (per the b6 contract — confirm exact routes/shapes there)
Wire each via `clientApi`; the names below are the expected commands/queries from the digest — bind to
whatever the contract publishes:
- `GET .../nurse/verification``GetVerificationStatusQuery` — the aggregate + per-step list driving B3/B6.
- `POST .../nurse/verification/start``StartNurseVerificationCommand` — seeds the steps (call from the
B3 "start / continue" CTA when status is `not_started`).
- `POST .../nurse/verification/identity``RunIdentityKycCommand` — national-ID + card image + liveness
selfie; drives B4. (Server also runs `shahkar_match` off the bound national-ID — surface both steps.)
- `POST .../nurse/verification/moh-license` (+ `.../ino`, `.../criminal-record` if split) →
the credential-submit commands behind B5.
- `POST .../nurse/verification/document` (or a presigned-URL flow) → the `IObjectStorage`-backed upload
used by every document step.
- The **bank step** (`bank_account_verification`) is submitted on the **existing f2 bank-account screen**
this phase only renders its status in the checklist and **deep-links** to that screen.
- `GET .../nurse/{id}/badge` (public) → `GetVerifiedBadgeQuery` — feeds the trust badge.
### 3.3 Screens (under the nurse shell `(private-routes)` nurse subtree)
Invoke `frontend-designer` for each. RTL-first; the screens live under the nurse route group from f0.
- **B3 · وضعیت احراز هویت — status checklist** (the hub).
- Overall progress meter **"X از Y"** (X = passed required steps, Y = total required) — reuse the f0
stepper/progress header.
- An **ordered, stepped checklist**, one row per step from `steps[]`, each with a **status chip** (reuse
the f0 status chip) in the five per-step states: `not_started`/locked-next (grey "بعدی"), `pending`
(amber "در انتظار"), `in_review` (amber "در حال بررسی"), `passed` (green "تاییدشده"), `rejected`
(red "رد شد" + the reason). Render **step 1 (mobile) as `passed`** from auth state.
- A **"what's blocking go-live" summary** + a single **continue CTA** ("ادامه مرحله N") that routes to
the next actionable step. When `not_started`, the CTA calls `useStartVerification` first.
- States: loading skeleton, error, and the terminal **`approved` state** (all passed → "احراز هویت
تکمیل شد، می‌توانید پروفایل را منتشر کنید" with a link to publish).
- **B4 · تایید هویت — identity submit.**
- **کد ملی (national-ID)** field with format validation (10-digit, checksum); **upload national-ID card
image** (camera/gallery via the document uploader §3.4); **liveness selfie** capture (camera; handle
permission-denied / retry / vendor-timeout states honestly).
- The honest auto note "استعلام خودکار از ثبت احوال" (auto civil-registry query) — this check *is*
performed (vendor-mocked), so the copy is allowed.
- Submit → `useSubmitIdentity`; on success the `identity_kyc` (and server-run `shahkar_match`) steps move
to `pending`/`in_review`, the cache invalidates, and the user returns to B3. Surface the **shared-SIM**
Shahkar failure as a clear, **non-accusatory** message, and the national-ID-mismatch failure on its step.
- **B5 · مدارک حرفه‌ای — professional credentials.**
- **شماره نظام پرستاری (INO number)** field; **upload پروانه/کارت نظام پرستاری** (the MoH competency
license — the single most important credential) via the uploader; **education certificate** upload
(shown with the uploaded-✓ state when present); **specialty chips** (multi-select: سالمندان, ICU, +
افزودن — an add-your-own chip input).
- Structured fields the registry needs where the contract asks for them (license number, issuing
authority, holder name as printed, issue/expiry date — **Shamsi date picker, stored UTC**).
- Submit → `useSubmitCredentials`; the credential steps move to `in_review` (manual admin review). Copy
must reflect manual review, not an automated authority confirmation.
- **B6 · در حال بررسی — under review.**
- The waiting state: "مدارک شما در حال بررسی است" + the **2448h** expectation + a **mini-checklist**
summarizing which steps are passed vs in-review (identity verified / professional docs in review /
bank account pending). Reuses the same per-step chips as B3, condensed. CTA "مشاهده وضعیت" → B3.
- This is the post-submission resting screen; B3 is the canonical source — B6 is a focused view of the
same `VerificationStatus`, not a second fetch with different state.
- **Trust badge on the nurse profile.** Render the **verified / unverified / expired** badge (the
"✓ تاییدشده" mark) on the **nurse's own profile** (f2) and export a small `<TrustBadge status=…/>`
shared component so **search results (C2) and the public nurse profile (C3) in f6 reuse it**. Source it
from `GetVerifiedBadgeQuery`. `expired` (a required credential lapsed) shows distinctly from `unverified`.
- **Publish gate.** Wire the f4 "انتشار پروفایل / go-live" action: when the aggregate status is not
`approved`, the publish CTA is **disabled with a blocked-until-verified explanation** ("برای انتشار،
احراز هویت را کامل کنید") that links to B3. This is the front-end enforcement of "not bookable until
verified" — the server enforces it too; the UI must not let a nurse *believe* they're live when they're not.
### 3.4 Document uploader (shared composite component)
Build a reusable **`<DocumentUpload>`** at the shared level (`src/components/…`, co-located `*.test.tsx`)
composed from MUI/`App*` primitives — used by every document step (national-ID card, license, education,
criminal record):
- **Client-side validation** before upload: file **type** (jpg/png/pdf per the contract) and **size cap**;
reject with a clear field error.
- **Upload states:** idle → selecting → **uploading (progress %)** → processing/hashing → success
(thumbnail/✓ + file name) → **error (retry)**. Wire progress from the upload mutation.
- **Re-upload on reject:** when a step is `rejected`, the uploader shows the prior file's status and a
re-upload affordance.
- Returns the server's stored **document metadata** (storage key, name) — never holds or displays raw
bytes beyond a local preview.
### 3.5 i18n namespace `verification` (both locales)
Fill the f0-reserved `verification` namespace in **both** `messages/en.json` and `messages/fa.json`, in
sync: step labels and per-step status labels (one key per `code` and per status — **never** derive a label
from the enum string), the B3/B4/B5/B6 screen copy, the uploader states, the honesty-sensitive copy
(manual-review vs auto-query), the blocked-until-verified message, the trust-badge labels, and the
shared-SIM / mismatch / format error messages. `fa` is default and RTL.
### Deferred (do not build here)
- The **admin verification review queue** (pass/reject a manual step, the doc viewer, credential-entry
form) → **(DEFERRED to [`frontend-phase-15-b15.md`](./frontend-phase-15-b15.md))**, the admin backoffice.
This phase only triggers the mock approval to *observe* the state change (see §7); it does not build the
admin UI.
- **Credential renewal/expiry prompts** beyond rendering the `expired` badge state → the nurse renewal
prompt UI is **(DEFERRED)**; the server's `CredentialExpiryScannerJob` (b6) raises the alert.
- **Video interview** (B6 mentions it) — not a built step at MVP; reflect it only as a grey "next" row if
the contract returns it; otherwise omit. Do not invent an interview-scheduling flow **(DEFERRED)**.
- Customer national-ID KYC — out of scope (the product gates only nurses) **(DEFERRED)**.
## 4. Mocks & seams in this phase
This phase **introduces no new front-end seam of its own** beyond the standard `services/{domain}` data
seam — the verification *vendors* (identity KYC, Shahkar, MoH/INO license, criminal record, IBAN ownership,
object storage) are **mocked server-side** behind DI seams owned by **backend-phase-6** (`IIdentityKycProvider`,
`IShahkarVerifier`, `ICredentialVerifier`, `IBankAccountOwnershipVerifier`, `IObjectStorage`) — the front
end consumes them only through the b6 contract, exactly as if they were real. **Reuse the `services/{domain}`
mock-`clientApi` pattern from f0:** if the b6 contract or a specific shape isn't published when you start,
build a **mock `clientApi`** behind the `services/verification` seam that returns realistic, contract-shaped
`VerificationStatus` (e.g. identity → `in_review`, then flippable to `passed`), record it in your phase
report + the [mock registry](../../shared-working-context/reports/mocks-registry.md), and **append the gap
to [`for-backend.md`](../../shared-working-context/frontend/requests/for-backend.md)**. The swap to the real
endpoint must be implementation-only (same hook signatures, same `queryKey`s) — no call-site changes.
## 5. Critical rules you must not get wrong
- **A nurse is NOT bookable and cannot publish until verified.** When the aggregate status is not
`approved`, the publish/go-live CTA is disabled with the blocked-until-complete explanation, and every
service variant stays inert. This front-end gate mirrors the server's guarded `is_verified` flip — never
let the UI imply a nurse is live before verification completes.
- **Honest copy — never advertise a check that isn't performed.** Manual steps (MoH/INO license, criminal
record) say "در حال بررسی / آپلود شد"; only the genuinely-performed automated checks (identity liveness,
civil-registry, Shahkar) may say "استعلام خودکار". A step is "تاییدشده" only when its status is `passed`.
This is a product constraint, not a style preference.
- **Per-step rejected-with-reason.** A `rejected` step renders its `rejection_reason` and a clear
re-submit/re-upload path — never a dead end. The shared-SIM Shahkar failure is an explicit, handled,
**non-accusatory** state, not a generic error.
- **The data is data-driven — render `steps[]` from the contract, don't hardcode the six steps.** The
pipeline is rows server-side; the UI iterates the returned ordered list and maps each `code`/`status` to
a label + chip. A new step type appearing in the response must render without a code change.
- **Reuse the shared stepper and status chip from f0** — do not re-implement them. Concrete MUI primitives
stay MUI; the new shared pieces (`<DocumentUpload>`, `<TrustBadge>`) live at the shared level with tests.
- **Caching is a feature.** B3 and B6 read the *same* `VerificationStatus` query — one cached source, two
views; every submit/upload mutation **invalidates** the status (and badge) so the checklist updates from
cache, never an extra manual refetch. Minimise re-renders (stable refs, `select` for slices).
- **RSC/client boundary + RTL + both locales + tokens.** No `next/headers`/`next-intl/server` in client
components; design RTL-first (`fa` default); every string a key in both locale files; colours from
`tokens.css` (the chip variants map to the wireframe's green/amber/grey/red legend); MUI v9 API only.
- **Document bytes are never shown from the API** — uploads return metadata only; a local preview before
upload is fine, but the rendered "uploaded" state is driven by server metadata, not retained bytes. The
national-ID and credential numbers are sensitive — show **masked** values where the contract masks them.
- **Dates:** credential issue/expiry are UTC on the wire, **Shamsi for display** (use the f0 date util).
## 6. Definition of Done
The shared [definition-of-done.md](../_shared/definition-of-done.md), plus:
- [ ] `services/verification` exists with the f0/f4 shape; types derived from
[`verification.md`](../../contracts/domains/verification.md) (not guessed); mutations invalidate the
status/badge query; one hook per file; no raw `fetch()`.
- [ ] B3 (status checklist), B4 (identity), B5 (credentials), B6 (under review) render under the nurse
shell, RTL, both locales in sync, reusing the f0 stepper + status chip.
- [ ] `<DocumentUpload>` and `<TrustBadge>` are shared components with co-located `*.test.tsx`; uploader
enforces type/size, shows progress, and supports re-upload on reject.
- [ ] The publish/go-live CTA from f4 is gated: disabled + blocked-until-verified message until `approved`.
- [ ] Honesty copy verified: manual steps never claim an automated authority check; a step shows
"تاییدشده" only when `passed`.
- [ ] Rejected steps show their reason and a re-submit path; shared-SIM is a clear non-accusatory state.
- [ ] `npm run check` green; `npm run test:ci` green (shared components added/touched); `en.json`/`fa.json`
in sync.
- [ ] `client/CLAUDE.md` *Project Structure* updated for the new route subtree + `services/verification`
+ the new shared components.
## 7. How to test (what a human can verify after this phase)
Run `npm run dev` (and the b6 server, or the mock `clientApi` if b6 isn't merged). Sign in as a `nurse`.
- **Checklist hub:** open the nurse verification screen → **B3** shows the "X از Y" meter, step 1 (mobile)
already `passed`/green, the rest `not_started`. The continue CTA routes to the next step (calling
`start` first when `not_started`).
- **Submit identity:** **B4** → enter a valid کد ملی, upload a national-ID card image (watch the
uploader's progress → success), capture a liveness selfie, submit → the `identity_kyc` and
`shahkar_match` steps move to **`pending`/`in_review`** on B3 **without a manual refresh** (cache
invalidation). Try an invalid national-ID → field error; trigger the mock shared-SIM number → a clear
non-accusatory Shahkar message.
- **Submit credentials:** **B5** → enter the نظام پرستاری number, upload the license + education cert, add
specialty chips, submit → the credential steps move to **`in_review`**; the UI lands on **B6** showing
"در حال بررسی", the 2448h note, and the mini-checklist.
- **Approval flips verified:** trigger the b6 mock admin approval (the mock provider/test endpoint, or set
the mock `clientApi` to return `approved`) → re-open B3: aggregate is **`approved`**, all required steps
`passed`, the **trust badge** shows **verified** on the nurse profile, and the **publish CTA is now
enabled** (was blocked-until-verified before).
- **Rejected step:** make a step return `rejected` (mock) → its row shows **the rejection reason** and a
working re-upload/re-submit path; the publish CTA stays blocked.
- **Quality:** `npm run check` and `npm run test:ci` pass; toggle locale → RTL/strings flip correctly;
dark mode renders; React Query Devtools shows one `verification.status` query feeding both B3 and B6 and
invalidating on each mutation.
## 8. Hand off & document (close the phase)
- **Docs:** update the *Project Structure* tree in [`../../../client/CLAUDE.md`](../../../client/CLAUDE.md)
for the nurse verification route subtree, `services/verification`, and the new shared components
(`<DocumentUpload>`, `<TrustBadge>`); add a one-line note that the trust badge is the reusable component
f6 consumes. If you discover a verification rule the product docs don't capture, record it in
[`../../../product/business/02-nurse-verification.md`](../../../product/business/02-nurse-verification.md)
(don't invent — record decisions).
- **Contracts:** **consume** [`../../contracts/domains/verification.md`](../../contracts/domains/verification.md)
(frontend produces none). Any missing/ambiguous shape (e.g. whether credential submit is one endpoint or
split, the exact document-upload flow, the badge payload) → append to
[`../../shared-working-context/frontend/requests/for-backend.md`](../../shared-working-context/frontend/requests/for-backend.md);
do **not** edit backend files.
- **Handoff & report:** append your phase summary to
[`../../shared-working-context/frontend/STATUS.md`](../../shared-working-context/frontend/STATUS.md);
write [`../../shared-working-context/reports/frontend-phase-5-report.md`](../../shared-working-context/reports/frontend-phase-5-report.md)
— what was built, **what is now testable and exactly how** (the §7 steps), what is mocked client-side
(the `services/verification` mock `clientApi`, if used) and how it swaps to the real b6 endpoint, the
contract consumed + gaps filed, and the follow-up that f6 reuses the `<TrustBadge>`. Update the
[mock registry](../../shared-working-context/reports/mocks-registry.md) for any client-side mock.
- **Memory:** save a `project` memory note for the non-obvious decisions — the data-driven step rendering,
the single-status-query-two-views (B3/B6) caching choice, the reusable `<TrustBadge>`/`<DocumentUpload>`
seams, and the publish-gate wiring — with a one-line pointer in `MEMORY.md`.