create mvp path
This commit is contained in:
@@ -15,13 +15,13 @@ step live in `docs/rules/server/` and this skill defers to them; it doesn't rest
|
||||
|
||||
| For | Read |
|
||||
|-----|------|
|
||||
| The dispatcher, folder shape, `OperationResult`, the controller skeleton, authorization | [docs/rules/server/cqrs.md](../../../docs/rules/server/cqrs.md) |
|
||||
| Projects, layers, the seam catalogue, startup wiring | [docs/rules/server/structure.md](../../../docs/rules/server/structure.md) |
|
||||
| EF Core, migrations, soft-delete, audit, config-as-rows, state machines, snapshots, uniqueness | [docs/rules/server/persistence.md](../../../docs/rules/server/persistence.md) |
|
||||
| Anything on the money path — ledger, refunds, BNPL, payouts, invoices | [docs/rules/server/money.md](../../../docs/rules/server/money.md) |
|
||||
| Auth, JWE, sessions, field encryption, tenancy | [docs/rules/server/identity.md](../../../docs/rules/server/identity.md) |
|
||||
| C# style, naming, async, testing | [docs/rules/server/conventions.md](../../../docs/rules/server/conventions.md) |
|
||||
| The gate, and what "done" means | [docs/rules/shared/git-and-gates.md](../../../docs/rules/shared/git-and-gates.md) |
|
||||
| The dispatcher, folder shape, `OperationResult`, the controller skeleton, authorization | [docs/rules/server/cqrs.md](../../../archive/docs/rules/server/cqrs.md) |
|
||||
| Projects, layers, the seam catalogue, startup wiring | [docs/rules/server/structure.md](../../../archive/docs/rules/server/structure.md) |
|
||||
| EF Core, migrations, soft-delete, audit, config-as-rows, state machines, snapshots, uniqueness | [docs/rules/server/persistence.md](../../../archive/docs/rules/server/persistence.md) |
|
||||
| Anything on the money path — ledger, refunds, BNPL, payouts, invoices | [docs/rules/server/money.md](../../../archive/docs/rules/server/money.md) |
|
||||
| Auth, JWE, sessions, field encryption, tenancy | [docs/rules/server/identity.md](../../../archive/docs/rules/server/identity.md) |
|
||||
| C# style, naming, async, testing | [docs/rules/server/conventions.md](../../../archive/docs/rules/server/conventions.md) |
|
||||
| The gate, and what "done" means | [docs/rules/shared/git-and-gates.md](../../../archive/docs/rules/shared/git-and-gates.md) |
|
||||
|
||||
**Stack:** ASP.NET Core (.NET 10), Clean Architecture, CQRS on `martinothamar/Mediator` (a source generator —
|
||||
**not MediatR**; there is no `IMediator` anywhere in this codebase), EF Core, FluentValidation, Mapster.
|
||||
@@ -34,22 +34,22 @@ step live in `docs/rules/server/` and this skill defers to them; it doesn't rest
|
||||
`Catalog`, `Verification`, `Search`, `Booking` (singular, pre-payment) / `Bookings` (plural, post-payment),
|
||||
`Payments`, `Refunds`, `Invoices`, `Bnpl`, `Payouts`, `Reviews`, `PatientCareRecords`, `Messaging`,
|
||||
`PartnerCenters`, `Configuration`, `Audit`, `Analytics`, `Holidays`, `Notifications`, `SupportAlerts`. Full
|
||||
list and the schema-per-area mapping: [structure.md](../../../docs/rules/server/structure.md) §2.
|
||||
list and the schema-per-area mapping: [structure.md](../../../archive/docs/rules/server/structure.md) §2.
|
||||
- **Command or query, or both?** A command mutates; a query reads. Most features are a matched pair (create
|
||||
+ get, or update + list).
|
||||
- **Find a sibling to mirror.** Grep the area's existing folder —
|
||||
`Features/<Area>/{Commands,Queries}/` — for a feature shaped like the one you're adding. Copying a live
|
||||
pattern beats inventing a new one.
|
||||
- **Does it touch money?** (ledger, refunds, invoices, BNPL, payouts) → read
|
||||
[money.md](../../../docs/rules/server/money.md) **first**. The invariants there (integer IRR, balanced
|
||||
[money.md](../../../archive/docs/rules/server/money.md) **first**. The invariants there (integer IRR, balanced
|
||||
ledger postings, webhook idempotency, snapshot-at-compute-time) are not suggestions.
|
||||
- **Does it add or change a table?** → read [persistence.md](../../../docs/rules/server/persistence.md) §5–7
|
||||
- **Does it add or change a table?** → read [persistence.md](../../../archive/docs/rules/server/persistence.md) §5–7
|
||||
before modeling it (soft-delete filters, forward-only status machines, snapshot fields, uniqueness
|
||||
patterns all have a house pattern — don't reinvent one).
|
||||
- **Does it need a new external dependency** (a vendor, a rail)? It becomes an interface in
|
||||
`Application/Contracts/`, mock in `CrossCutting/Seams/`, real in `CrossCutting/Seams/Real/`, selected by a
|
||||
`Seams:<rail>:Provider` config key that **falls closed to the mock**. See
|
||||
[structure.md](../../../docs/rules/server/structure.md) §3.
|
||||
[structure.md](../../../archive/docs/rules/server/structure.md) §3.
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +77,7 @@ Baya.Application/Features/<Area>/
|
||||
memory. Command: use `Include` only when you need navigation properties loaded to mutate the aggregate,
|
||||
access the DB through `IUnitOfWork`, and `CommitAsync` once at the end.
|
||||
|
||||
Full rules and the validator/OperationResult examples: [cqrs.md](../../../docs/rules/server/cqrs.md) §1–3.
|
||||
Full rules and the validator/OperationResult examples: [cqrs.md](../../../archive/docs/rules/server/cqrs.md) §1–3.
|
||||
|
||||
---
|
||||
|
||||
@@ -100,7 +100,7 @@ dotnet ef database update --project src/Infrastructure/Baya.Infrastructure.Persi
|
||||
```
|
||||
|
||||
Full patterns, with the exact uniqueness/snapshot/state-machine tables:
|
||||
[persistence.md](../../../docs/rules/server/persistence.md).
|
||||
[persistence.md](../../../archive/docs/rules/server/persistence.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -127,7 +127,7 @@ public sealed class MyFeatureController(ISender sender) : BaseController
|
||||
`SnakeCaseParameterTransformer` will produce, rename the method instead.
|
||||
- Pick the narrowest authorization that fits: none (truly public) → `[Authorize]` (any authenticated user) →
|
||||
`[Authorize(ConstantPolicies.DynamicPermission)]` (role/claim-gated admin action). Table and rate-limiting
|
||||
notes: [cqrs.md](../../../docs/rules/server/cqrs.md) §4.
|
||||
notes: [cqrs.md](../../../archive/docs/rules/server/cqrs.md) §4.
|
||||
|
||||
---
|
||||
|
||||
@@ -140,20 +140,20 @@ public sealed class MyFeatureController(ISender sender) : BaseController
|
||||
3. The recurring-job scheduler is dormant under `Testing`, so a background tick can't make an integration
|
||||
test flaky — you don't need to account for it.
|
||||
|
||||
Examples and the full testing convention: [conventions.md](../../../docs/rules/server/conventions.md) §8.
|
||||
Examples and the full testing convention: [conventions.md](../../../archive/docs/rules/server/conventions.md) §8.
|
||||
|
||||
---
|
||||
|
||||
## 6. Docs this feature triggers — in the same change
|
||||
|
||||
- **[`docs/integration/domains/<domain>.md`](../../../docs/integration/domains/index.md)** — add the new
|
||||
- **[`docs/integration/domains/<domain>.md`](../../../archive/docs/integration/domains/index.md)** — add the new
|
||||
endpoint with its verdict (`wired`/`unwired`/`phantom`), matching the client `services/` domain it belongs
|
||||
to.
|
||||
- **The OpenAPI snapshot** — regenerate `docs/integration/openapi/swagger.v1.json` per
|
||||
[openapi/README.md](../../../docs/integration/openapi/README.md) and update its provenance table (date,
|
||||
[openapi/README.md](../../../archive/docs/integration/openapi/README.md) and update its provenance table (date,
|
||||
commit, path/operation counts) in the same change. A snapshot with stale provenance is what that
|
||||
convention exists to prevent.
|
||||
- **[`docs/status/backlog.md`](../../../docs/status/backlog.md)** — tick the row if this closes a filed
|
||||
- **[`docs/status/backlog.md`](../../../archive/docs/status/backlog.md)** — tick the row if this closes a filed
|
||||
item. Never delete a row; a ticked row is the record that it shipped.
|
||||
- **A reference file in `docs/rules/server/`** — only if the feature introduces a genuinely new reusable
|
||||
pattern, seam, or base class. Don't add prose for a feature that just follows the existing pattern.
|
||||
@@ -164,4 +164,4 @@ Examples and the full testing convention: [conventions.md](../../../docs/rules/s
|
||||
|
||||
Run the server gate — `dotnet build Baya.sln` (**zero new warnings**) and `dotnet test Baya.sln` — and read
|
||||
your diff as if reviewing the PR. Full "what done means" checklist:
|
||||
[git-and-gates.md](../../../docs/rules/shared/git-and-gates.md) §2.
|
||||
[git-and-gates.md](../../../archive/docs/rules/shared/git-and-gates.md) §2.
|
||||
|
||||
@@ -10,14 +10,14 @@ description: >-
|
||||
|
||||
Exercising a flow proves something only if you know which half of the stack actually answered. This is the
|
||||
procedure; the facts it points at (ports, accounts, known failure modes) live in
|
||||
[docs/flows/testing-setup.md](../../../docs/flows/testing-setup.md) and are kept current there — don't copy
|
||||
[docs/flows/testing-setup.md](../../../archive/docs/flows/testing-setup.md) and are kept current there — don't copy
|
||||
them here, they will drift.
|
||||
|
||||
---
|
||||
|
||||
## 1. Boot it
|
||||
|
||||
The five-minute path, verbatim from [testing-setup.md](../../../docs/flows/testing-setup.md#the-five-minute-path):
|
||||
The five-minute path, verbatim from [testing-setup.md](../../../archive/docs/flows/testing-setup.md#the-five-minute-path):
|
||||
|
||||
```bash
|
||||
# API — mock SMS or request_otp 500s
|
||||
@@ -41,7 +41,7 @@ carries a `Last verified` stamp for exactly this reason.
|
||||
|
||||
A flow "working" through a mocked domain proves the UI, not the server. Before testing:
|
||||
|
||||
1. Open [docs/integration/domains/index.md](../../../docs/integration/domains/index.md) — the census table
|
||||
1. Open [docs/integration/domains/index.md](../../../archive/docs/integration/domains/index.md) — the census table
|
||||
names which of the 22 client `services/` domains are real vs **mock** (currently 15 real, 7 mock:
|
||||
`admin`, `bnpl`, `partnerCenter`, `patientRecords`, `payouts`, `refunds`, `verification`).
|
||||
2. A mocked domain is a `USE_<DOMAIN>_MOCK` flag in `client/src/services/<domain>/constants.ts` — check it
|
||||
@@ -55,7 +55,7 @@ A flow "working" through a mocked domain proves the UI, not the server. Before t
|
||||
## 3. Pick the right seeded account
|
||||
|
||||
Demo accounts, their roles, and what each one demonstrates are tabulated in
|
||||
[testing-setup.md](../../../docs/flows/testing-setup.md#demo-accounts) — read it there rather than assuming
|
||||
[testing-setup.md](../../../archive/docs/flows/testing-setup.md#demo-accounts) — read it there rather than assuming
|
||||
a phone number. One standing gap to route around: **the seeded admin accounts (`…020` `super_admin`,
|
||||
`…021` `finance`) get 403 on every real admin endpoint** (a `DynamicPermission` / role-literal mismatch).
|
||||
The admin backoffice is only testable against the client's mock; don't spend time trying to walk it against
|
||||
@@ -65,7 +65,7 @@ the real API without first checking whether that gap has been closed.
|
||||
|
||||
## 4. Walk the flow
|
||||
|
||||
[docs/flows/index.md](../../../docs/flows/index.md) is the atlas — one file per user-meaningful journey,
|
||||
[docs/flows/index.md](../../../archive/docs/flows/index.md) is the atlas — one file per user-meaningful journey,
|
||||
each answering exactly three questions: what it does, what's mocked *for that journey specifically*, and how
|
||||
to test it. Open the one file that matches what you're testing rather than guessing the steps; it's the
|
||||
one place gap numbers and REQ references for that journey are tracked.
|
||||
@@ -79,7 +79,7 @@ one place gap numbers and REQ references for that journey are tracked.
|
||||
on a request promptly, or create a fresh one rather than trying to reuse an old test artifact.
|
||||
- **The OTP endpoints are rate-limited together.** `request_otp` and `verify_otp` share one bucket, 5 calls
|
||||
per 60 s per IP — a login is 2 calls, so that's **two logins per minute, total**. Space scripted logins
|
||||
≥ 40 s apart (see [testing-setup.md](../../../docs/flows/testing-setup.md#scripting-logins) for a working
|
||||
≥ 40 s apart (see [testing-setup.md](../../../archive/docs/flows/testing-setup.md#scripting-logins) for a working
|
||||
script) or you'll 429 and misread it as a bug.
|
||||
|
||||
---
|
||||
@@ -102,6 +102,6 @@ because the world was seeded days ago:
|
||||
|
||||
Name the account you used, the domain's mock/real status, and the exact response (status code, error
|
||||
message) rather than "it worked" — the troubleshooting table in
|
||||
[testing-setup.md](../../../docs/flows/testing-setup.md#troubleshooting) exists because several failure
|
||||
[testing-setup.md](../../../archive/docs/flows/testing-setup.md#troubleshooting) exists because several failure
|
||||
modes here look identical to an unrelated bug (a rate limit looks like a crash; `/healthz/ready` failing on
|
||||
Windows looks like the app is down). Check it before filing something as a new defect.
|
||||
|
||||
@@ -16,7 +16,7 @@ color schemes on the first try.
|
||||
|
||||
**Precedence.** This skill is the **design** contract — brand, tone, and the visual
|
||||
decisions. The **engineering** contract is [client/CLAUDE.md](../../../client/CLAUDE.md)
|
||||
(hard rules) plus [docs/rules/client/](../../../docs/rules/client/) (one reference file
|
||||
(hard rules) plus [docs/rules/client/](../../../archive/docs/rules/client/) (one reference file
|
||||
per area). Where the two overlap — tokens, typography, the component library, shells,
|
||||
icons — **`docs/rules/client/` is authoritative and this skill defers to it.** Read the
|
||||
relevant one before touching layout, provider, or data code; don't restate it here, and
|
||||
@@ -24,10 +24,10 @@ never violate it.
|
||||
|
||||
| For | Read |
|
||||
|-----|------|
|
||||
| Tokens, palette, dark mode, RTL, fonts, motion | [docs/rules/client/theme.md](../../../docs/rules/client/theme.md) |
|
||||
| The `App*` library, shells, navigation, icons | [docs/rules/client/components.md](../../../docs/rules/client/components.md) |
|
||||
| Copy and Persian orthography | [docs/rules/client/i18n.md](../../../docs/rules/client/i18n.md) |
|
||||
| Forms | [docs/rules/client/forms.md](../../../docs/rules/client/forms.md) |
|
||||
| Tokens, palette, dark mode, RTL, fonts, motion | [docs/rules/client/theme.md](../../../archive/docs/rules/client/theme.md) |
|
||||
| The `App*` library, shells, navigation, icons | [docs/rules/client/components.md](../../../archive/docs/rules/client/components.md) |
|
||||
| Copy and Persian orthography | [docs/rules/client/i18n.md](../../../archive/docs/rules/client/i18n.md) |
|
||||
| Forms | [docs/rules/client/forms.md](../../../archive/docs/rules/client/forms.md) |
|
||||
|
||||
**Stack:** Next.js 16 (App Router, Turbopack) · React 19 · MUI v9 (`@mui/material`) ·
|
||||
Emotion (RTL via `stylis-plugin-rtl`) · next-intl v4 · notistack. Everything below
|
||||
@@ -128,7 +128,7 @@ these; they're define-only in CSS):
|
||||
- **Map** — `--bal-pin-shadow`, the address-picker pin.
|
||||
|
||||
Full catalogue, with what each group backs:
|
||||
[docs/rules/client/theme.md](../../../docs/rules/client/theme.md) §2.
|
||||
[docs/rules/client/theme.md](../../../archive/docs/rules/client/theme.md) §2.
|
||||
|
||||
---
|
||||
|
||||
@@ -188,7 +188,7 @@ Beyond the `App*` wrappers there is a **state kit** — `EmptyState`, `ErrorStat
|
||||
`InitialsAvatar`, `FormDialogShell` — with **one pattern per state**. Never hand-roll a
|
||||
dashed-border "nothing here" block or a per-screen pager; and **an error state is never an
|
||||
empty state.** Catalogue in
|
||||
[docs/rules/client/components.md](../../../docs/rules/client/components.md).
|
||||
[docs/rules/client/components.md](../../../archive/docs/rules/client/components.md).
|
||||
|
||||
For layout/spacing use MUI primitives directly: `Box`, `Stack`, `Container`, `Grid`,
|
||||
`Paper`, `Card`. Use the `spacing`/`sx` system (theme spacing unit = 8px) — never inline
|
||||
@@ -197,18 +197,18 @@ pixel margins for rhythm.
|
||||
**New shared component?** Put it in `src/components/<Name>/<Name>.tsx` with an
|
||||
`index.tsx` barrel, follow the `App*` prop-spreading + JSDoc style of `AppButton.tsx`,
|
||||
and add a co-located `.test.tsx` (mandatory for anything imported in >1 place — see
|
||||
[docs/rules/client/testing.md](../../../docs/rules/client/testing.md); wrap with
|
||||
[docs/rules/client/testing.md](../../../archive/docs/rules/client/testing.md); wrap with
|
||||
`<ThemeProvider>`, never mock MUI). If it goes at the top of the `@/components/common`
|
||||
barrel, prefer **caller-owned copy** (required `title`/`body`/`retryLabel` string props)
|
||||
over calling `useTranslations` inside it — `next-intl` is ESM-only and poisons every test
|
||||
that transitively imports the barrel. `ErrorBoundary`/`ErrorState` are the model;
|
||||
[components.md](../../../docs/rules/client/components.md) has the why.
|
||||
[components.md](../../../archive/docs/rules/client/components.md) has the why.
|
||||
|
||||
**Any form with more than one field is a react-hook-form form**, bound through the
|
||||
`@/components/common/form` wrappers (`RhfTextField`, `RhfChipSelect`,
|
||||
`RhfJalaliDateField`, `RhfControlGroup`) and grouped into `FormSection`s. A single-field
|
||||
control is state, not a form. Full pattern:
|
||||
[docs/rules/client/forms.md](../../../docs/rules/client/forms.md).
|
||||
[docs/rules/client/forms.md](../../../archive/docs/rules/client/forms.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -341,14 +341,14 @@ Every screen/component you produce must satisfy **all** of these:
|
||||
switches automatically. Verify on both schemes — never assume a light background.
|
||||
4. **Tokens, not hexes.** No raw color literals in `sx`/`styled`/components (§2).
|
||||
5. **Constants, not magic values.** Cookie names, routes, repeated dimensions, event
|
||||
names → named constants ([components.md](../../../docs/rules/client/components.md) §5).
|
||||
names → named constants ([components.md](../../../archive/docs/rules/client/components.md) §5).
|
||||
6. **Use the wrappers** (§4) and the **icon registry** (§6) before bare MUI.
|
||||
7. **Shared component ⇒ co-located test** (§4).
|
||||
8. **MUI v9 API only.** No v5/v6-era props (e.g. `Stack` `useFlexGap`, `storageWindow`).
|
||||
Avoid deprecated APIs that throw.
|
||||
9. **Persian copy follows the style guide** — «بالینیار» with a ZWNJ, تأیید with a hamza,
|
||||
جستجو in one form, formal شما. `npm run lint:copy` fails the gate on a banned variant.
|
||||
Glossary and the full rules: [i18n.md](../../../docs/rules/client/i18n.md) §4.
|
||||
Glossary and the full rules: [i18n.md](../../../archive/docs/rules/client/i18n.md) §4.
|
||||
10. **A screen never fabricates a figure.** A summary reads only off a query that already
|
||||
answers it; a count still in flight is omitted, never faked or defaulted.
|
||||
|
||||
@@ -366,10 +366,10 @@ Every screen/component you produce must satisfy **all** of these:
|
||||
5. **Verify the four axes:** `/fa` (RTL) and `/en` (LTR) × light and dark. The default
|
||||
route is `/fa` — start there.
|
||||
6. **Tests** for any new shared component; **never** add a layout above `[locale]`
|
||||
(breaks locale/dir — see [structure.md](../../../docs/rules/client/structure.md)).
|
||||
(breaks locale/dir — see [structure.md](../../../archive/docs/rules/client/structure.md)).
|
||||
7. Data/fetch/auth/cookies/toasts → follow
|
||||
[services.md](../../../docs/rules/client/services.md) and
|
||||
[auth.md](../../../docs/rules/client/auth.md) (`serverFetch`/`clientFetch`,
|
||||
[services.md](../../../archive/docs/rules/client/services.md) and
|
||||
[auth.md](../../../archive/docs/rules/client/auth.md) (`serverFetch`/`clientFetch`,
|
||||
`@/lib/cookies/*`, `dispatchToast`/`useSnackbar`). Don't reinvent these.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user