273 lines
14 KiB
Markdown
273 lines
14 KiB
Markdown
# Phase 3 — The flow atlas (verified)
|
||
|
||
**Depends on:** Phase 0, Phase 2 · **Blocks:** Phase 4 · **Size:** 2–4 sessions — the biggest phase
|
||
|
||
## Goal
|
||
|
||
Build **the one go-to place** that answers, for every flow in the product:
|
||
|
||
1. What is it, and who does it?
|
||
2. **Is it actually implemented — really, or only mocked?**
|
||
3. What screens and endpoints does it use?
|
||
4. **How do I test it, step by step, with which account?**
|
||
5. What's known to be broken or missing?
|
||
|
||
This is the phase where "verify the load-bearing claims" earns its keep. The existing
|
||
[manual-testing-plan.md](../../dev/post-phase/manual-testing-plan.md) is the closest predecessor and
|
||
it is already wrong about bring-up (it tells you to set `user-secrets`, which the code no longer
|
||
reads). Copying it forward would reproduce the problem this chain exists to fix.
|
||
|
||
**Run this phase in slices.** One session does `testing-setup.md` + 4–6 flows. Later sessions pick up
|
||
the next slice. The index table tracks which flows are done.
|
||
|
||
---
|
||
|
||
## Inputs
|
||
|
||
- [dev/post-phase/manual-testing-plan.md](../../dev/post-phase/manual-testing-plan.md) — flow
|
||
walkthroughs, test accounts, the seeded world, the mock-vs-real map, the numbers the UI must respect
|
||
- [dev/post-phase/refinement/RUNBOOK.md](../../dev/post-phase/refinement/RUNBOOK.md) — bring-up, demo
|
||
accounts, login round-trip, reset, troubleshooting
|
||
- [dev/shared-working-context/reports/mocks-registry.md](../../dev/shared-working-context/reports/mocks-registry.md)
|
||
- `docs/integration/` — from Phase 2, the endpoint truth
|
||
- `product/business/*` — the 14 requirement areas; what "correct" means
|
||
- The seeder code (`DemoWorldSeeder`, `DemoLifecycleSeeder`) — **the authority on test accounts and
|
||
seeded state**, over any doc
|
||
- `client/src/app/[locale]/**` — 83 routes across `(customer)`, `(customer-focused)`, `nurse`,
|
||
`admin`, `partner`, `(public-routes)`
|
||
- `client/src/services/*/` — 22 domains, each with a mock/real flag
|
||
|
||
## Outputs
|
||
|
||
```
|
||
docs/flows/
|
||
index.md the status table — every flow, one row
|
||
testing-setup.md bring-up, accounts, seeded world, OTP, reset, troubleshooting
|
||
<flow>.md one per flow (see the candidate list)
|
||
```
|
||
|
||
---
|
||
|
||
## Candidate flow list
|
||
|
||
Derived from the route groups, the 22 service domains, and the 14 business areas. Confirm and adjust
|
||
in the first session; the point is one file per *user-meaningful journey*, not per screen.
|
||
|
||
| # | Flow | Actor | Primary routes |
|
||
| --- | --- | --- | --- |
|
||
| 1 | `auth-login-otp` | all | `(public-routes)/login`, `select-role` |
|
||
| 2 | `public-front-door` | guest | `/`, `welcome`, `terms`, `privacy` |
|
||
| 3 | `onboarding-customer` | customer | onboarding, account |
|
||
| 4 | `care-circle-patients` | customer | patients / care-circle |
|
||
| 5 | `addresses-and-map` | customer | addresses, Neshan pin |
|
||
| 6 | `onboarding-nurse` | nurse | profile, bank account |
|
||
| 7 | `nurse-service-areas` | nurse | coverage, whole-city (`districtId = null`) |
|
||
| 8 | `nurse-verification` | nurse + admin | verification journey, document upload, review queue |
|
||
| 9 | `nurse-catalog-and-pricing` | nurse | services, variant builder |
|
||
| 10 | `search-and-discovery` | customer | C1–C3, filters, nurse profile |
|
||
| 11 | `booking-request` | customer + nurse | C4/C5, nurse inbox, countdown, accept/reject |
|
||
| 12 | `checkout-and-payment` | customer | C6, gateway return, confirmation, escrow |
|
||
| 13 | `bnpl-installments` | customer | D1–D5 |
|
||
| 14 | `booking-lifecycle-evv` | nurse + customer | check-in/out, two-stage clinical gate |
|
||
| 15 | `cancellation-and-refunds` | customer + admin | cancel, policy, refund settlement |
|
||
| 16 | `reviews` | customer | post-visit review, moderation |
|
||
| 17 | `patient-care-records` | nurse + customer | append-only records |
|
||
| 18 | `nurse-earnings-and-payouts` | nurse | earnings, weekly payout run |
|
||
| 19 | `messaging-tickets` | all | threads, `is_internal` boundary |
|
||
| 20 | `notifications` | all | bell, day-grouped list |
|
||
| 21 | `admin-backoffice` | admin | config, holidays, audit, RBAC, queues |
|
||
| 22 | `partner-center` | partner | the separately-scoped portal |
|
||
| 23 | `account-and-settings` | all | profile, theme/language (settings-only since iteration 1) |
|
||
|
||
---
|
||
|
||
## Steps
|
||
|
||
### 1. Write `testing-setup.md` — and **actually boot it**
|
||
|
||
Do not transcribe RUNBOOK.md. Follow it, note where it is wrong, and write what really happens.
|
||
|
||
Must cover:
|
||
|
||
- Prerequisites and the two-terminal run (`dotnet run` + `npm run dev`).
|
||
- **Configuration** — the current, correct story: config in `appsettings.*.json` and `.env.*`;
|
||
`user-secrets` **is not used and is not read** (`<UserSecretsId>` was removed). The four crypto
|
||
values still matter and must match whatever the target DB was encrypted under — booting with
|
||
different `Seams:FieldEncryption` keys makes every phone lookup miss and every PII read throw
|
||
`Padding is invalid`. Say where they live now.
|
||
- **Which database.** The dev config has pointed at a *remote* SQL Server. Confirm the current
|
||
target, and give the local-DB alternative.
|
||
- **Test accounts** — read them out of the seeder, not the old doc. Note the phone-OTP admins
|
||
(`09120000020` super_admin, `09120000021` finance) and what each demo account is set up to
|
||
demonstrate.
|
||
- **Getting the OTP** — server console (`MOCK SMS — OTP code …`), `GET /api/v1/dev/last_otp/{phone}`,
|
||
or the Telegram relay. Note the limits: 120 s resend window, 5 wrong attempts, 60 s validity, and
|
||
that the OTP endpoints are IP rate-limited (scripted logins will 429).
|
||
- **The seeded world** — what `DemoLifecycleSeeder` builds (the 8-booking world), and the fact that
|
||
**time-relative scenarios age out** and need a reseed.
|
||
- **Reset** — the drop-and-reseed procedure, verified.
|
||
- **Troubleshooting** — the failures you actually hit while doing the above.
|
||
|
||
### 2. Build the mock-vs-real map — from code
|
||
|
||
The single most important input to every flow file, and the thing most likely to be stale in the
|
||
docs. For each of the 22 client `services/` domains, read the code:
|
||
|
||
- Is the mock flag on or off?
|
||
- If real, does the whole domain hit the API, or only part of it?
|
||
- Server-side: which seams are mocked (SMS, payment gateway, BNPL provider, object storage,
|
||
geocoder, search) and which have a real adapter available behind config (refinement-phase-8)?
|
||
|
||
Cross-check `mocks-registry.md` and **correct it** — Phase 0 already flagged that the registry has
|
||
had stale rows before. Put the result in `docs/flows/index.md` as a column, and in each flow file as
|
||
a header block.
|
||
|
||
Known suspects from the hardening audit (2026-07-16, never ticked off — verify each against current
|
||
code, do not assume): verification fully mocked while catalog/search are real; the refunds mock
|
||
reading a retired store; the BNPL wizard on a disconnected store; nurse earnings fabricated despite
|
||
live endpoints; `patientRecords` id-type mismatch.
|
||
|
||
### 3. Write one file per flow
|
||
|
||
Template — keep each under ~200 lines:
|
||
|
||
```markdown
|
||
# Flow — <name>
|
||
|
||
> Last verified: <date> against <commit>
|
||
|
||
**Actor(s):** · **Status:** built | partial | mocked | not started
|
||
**Business source:** product/business/NN-….md
|
||
|
||
## What it does
|
||
Two or three sentences. The user's intent, not the implementation.
|
||
|
||
## Screens
|
||
| Step | Route | Component/notes |
|
||
|
||
## API
|
||
| Call | Endpoint | Notes |
|
||
Link to docs/integration/domains/<domain>.md — don't restate shapes here.
|
||
|
||
## Rules that must hold
|
||
The load-bearing numbers and invariants, with their product/ source.
|
||
(e.g. commission 0.15, VAT 0.10 on commission only; forward-only status;
|
||
escrow released after confirmed check-out; whole-city = districtId NULL)
|
||
|
||
## How to test
|
||
1. Log in as <account> (see testing-setup.md)
|
||
2. …
|
||
**Expect:** …
|
||
|
||
## Known gaps
|
||
- [BL-xxx] … (filled by Phase 4; leave a plain list here for now)
|
||
```
|
||
|
||
**Verification standard per flow.** For each, do at least the cheap check — trace the route → service
|
||
→ endpoint → handler and confirm the chain is real. For the money flows (11–15, 18) and auth (1), do
|
||
the expensive check too: run the walkthrough against a booted app. If you cannot run it, mark the
|
||
flow's status `UNVERIFIED` in the index and say why — an honest gap beats a confident guess.
|
||
|
||
### 4. Write `index.md`
|
||
|
||
The status table, and nothing else of substance:
|
||
|
||
| Flow | Actor | Status | Client | Server | Verified | File |
|
||
| --- | --- | --- | --- | --- | --- | --- |
|
||
| booking-request | customer + nurse | built | real | real | 2026-…-… | [link] |
|
||
|
||
Statuses: `built` (end-to-end real) · `partial` (real but with gaps) · `mocked` (UI real, data fake) ·
|
||
`not started`. `Client`/`Server` columns say `real`/`mock` independently — a flow can have a real UI
|
||
on a mocked service, which is exactly the trap the hardening audit found.
|
||
|
||
---
|
||
|
||
## Verification
|
||
|
||
- [x] `testing-setup.md` was executed, not transcribed — the API and the client were booted, all 8 demo
|
||
accounts logged in over the real phone-OTP round-trip, and a production client build was run to settle
|
||
the root-path question. 10 stale instructions in the predecessor docs were found and corrected.
|
||
- [x] Every flow file has a status backed by a code trace. The 7 money/auth flows were additionally walked
|
||
against the running API by an adversarial second agent. **0 flows are `UNVERIFIED`.**
|
||
- [x] The mock-vs-real map was derived from code (22 domains, 153 seam operations; 26 server seams) and
|
||
**17 disagreements with `mocks-registry.md` are logged** in `docs/flows/index.md`. That file now
|
||
carries a banner pointing at the correction.
|
||
- [x] No flow file restates an API shape. 126 links into `docs/integration/` instead; the only JSON block
|
||
anywhere is one `request_otp` response in `testing-setup.md`, used as a bring-up example.
|
||
- [x] The index accounts for all 22 client service domains, all 14 business areas and all 83 routes. Area
|
||
13 (Tax, Invoicing & Legal) is flagged as the weakest coverage and is a Phase 4 item.
|
||
- [x] Every flow file carries a `Last verified:` line. 477 relative links checked, 0 broken.
|
||
|
||
## Definition of done
|
||
|
||
You can hand someone `docs/flows/testing-setup.md` and `docs/flows/index.md` and they can test the
|
||
product without asking you a single question — and without hitting an instruction that no longer works.
|
||
|
||
## Handoff
|
||
|
||
**Done 2026-08-02 against `c841bde`, in one session rather than the planned 2–4 slices.**
|
||
|
||
### What was produced
|
||
|
||
`docs/flows/` — 25 files, ~3 500 lines: `index.md`, `testing-setup.md`, and **all 23 flow files**. The
|
||
candidate list needed no adjustment; every one turned out to be a real, distinct journey.
|
||
|
||
**Status spread: 1 `built` · 15 `partial` · 7 `mocked` · 0 `not started` · 0 `UNVERIFIED`.**
|
||
Only `auth-login-otp` is end-to-end real and observed working.
|
||
|
||
### How it was verified
|
||
|
||
The stack was booted and stayed up throughout. Every flow got the cheap check (route → hook → seam →
|
||
endpoint → controller → handler, cited `file:line`). The 7 money/auth flows got the expensive check *twice*:
|
||
once by the author, then by an independent adversarial agent told to refute the status claim. That second
|
||
pass was worth its cost — it caught a fabricated test count, five wrong `file:line` refs, two factually
|
||
wrong gaps, a business-rule citation that asserted the **opposite** of the claim it was supporting, a
|
||
fabricated source for a money constant, a self-contradicting headline, and a test step that was false
|
||
against the running world. All were corrected in place.
|
||
|
||
Verification also **mutated the shared demo DB**: booking requests 21–24 were created and driven through
|
||
accept / reject / cancel / convert to prove those paths work. They carry fresh dates (2026-08-06…08-12) and
|
||
are the only non-aged requests in the world.
|
||
|
||
### The six findings that matter most
|
||
|
||
1. **The admin backoffice cannot reach its own server.** All 16 admin GET operations `403` for both seeded
|
||
admin accounts. `DynamicPermissionService.CanAccess` grants on the literal role `"admin"` or a
|
||
per-controller `DynamicPermission` claim; the demo admins hold `super_admin`/`finance`, and **no code
|
||
anywhere writes that claim**. `USE_ADMIN_MOCK = true` hides it completely.
|
||
2. **34 of 153 client seam operations target routes that do not exist server-side** — they would `404` the
|
||
day a flag is flipped. 12 more fabricate or client-derive a value. The flag is not the honesty signal.
|
||
3. **No card payment can complete from a browser.** `MockPaymentProvider` redirects to a non-existent host.
|
||
The server money path is correct and idempotent; only the last hop is missing.
|
||
4. **VAT is computed two different ways on two surfaces** — carved out of the commission at checkout,
|
||
added to it on the invoice — differing by 341 IRR on one seeded booking. No `product/` file resolves it.
|
||
5. **Booking deadline timestamps ship without a timezone** (`DateTime`, not `DateTimeOffset`), so every
|
||
countdown the client renders is wrong by the UTC offset — 3.5 h in Tehran.
|
||
6. **The real BNPL rail is dead.** `SeedPaymentGatewaysAsync` seeds only a `Standard` gateway, never a
|
||
`Bnpl` one, so `checkout_bnpl/initiate` returns `400 "No active BNPL gateway is configured."` for every
|
||
request. The mocked UI hides it entirely.
|
||
|
||
Plus: the demo world is dated 2026-07-26 and has aged out (no `pending`/`accepted` request survived the
|
||
60-second expiry job); `/healthz/ready` is a genuine Windows-only code defect; and a re-login silently
|
||
invalidates every token previously issued to that account, so two people cannot share a demo phone.
|
||
|
||
### Input to Phase 4
|
||
|
||
**283 gaps** are recorded across the 23 files, each a plain bullet naming what breaks, for whom, and where
|
||
in the code. That list plus the 17 `mocks-registry.md` corrections is Phase 4's primary input. No `BL-###`
|
||
ids were invented — Phase 4 assigns them.
|
||
|
||
### What was left undone, and why
|
||
|
||
- **The local-Docker database path is documented but `UNVERIFIED`** — Docker is not installed on the
|
||
verification machine. The remote path was fully exercised instead.
|
||
- **The reset/reseed procedure was not executed.** It requires dropping a database shared with the
|
||
`balinyaar.ir` demo deployment; that is the owner's call, not a doc-writing side effect.
|
||
- **No screen was driven in a browser.** Everything client-side is a code trace plus HTTP-level probing of
|
||
the dev and production servers. Where that limits a claim, the file says so.
|
||
- **Backend seam rows in `mocks-registry.md` were not re-audited** row-by-row; the current server picture is
|
||
the seam table in `docs/flows/index.md` instead.
|
||
- `dev/post-phase/manual-testing-plan.md`, `dev/post-phase/refinement/RUNBOOK.md` and
|
||
`dev/shared-working-context/reports/mocks-registry.md` now carry supersession banners pointing at
|
||
`docs/flows/`. They were otherwise left intact, because `dev/` is history.
|