Files
baya-monorepo/archive/clarify-chain/phase-3-flow-atlas.md
T
2026-08-02 18:48:32 +03:30

273 lines
14 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.
# Phase 3 — The flow atlas (verified)
**Depends on:** Phase 0, Phase 2 · **Blocks:** Phase 4 · **Size:** 24 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` + 46 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 | C1C3, 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 | D1D5 |
| 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 (1115, 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 24 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 2124 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.