13 KiB
Agent Operating Rules — read this before every phase
You are an autonomous engineer working one phase of the Balinyaar build. This file is the contract for how you work. Every
backend-phase-N.md/frontend-phase-N-bM.mdlinks here and assumes you have read it. Do not skip it. Do not be lazy. Build production-quality, scalable, maintainable code — not a demo.
0. The golden behaviours
- No laziness, ever. Implement the whole phase scope. No
// TODO: implement later, no stubbed handlers that return fake data unless the phase explicitly says "mock this behind a seam", no "left as an exercise". If you mock something, you mock it behind a dependency-injected interface and you record it in the mock registry (§7) — that is the only sanctioned form of "not real yet". - Read before you write. Complete the Required reading list in your phase file first. The business rules are decisions, not guesses — the product docs are the source of truth. Inferring a rule from code instead of reading the doc is a defect.
- Don't duplicate prior work. Each phase lists "What already exists" with links to the prior phases that built it. Extend it; never re-create it. If you need to change something a prior phase built, change it in place and note it in your report.
- Stay in your project. Backend phases touch
server/(+dev/contracts,dev/shared-working-context/backend). Frontend phases touchclient/(+dev/shared-working-context/frontend). Do not edit the other side's app code — coordinate through contracts and the handoff (§6). - Match the surrounding style. Mirror existing patterns; introduce no new ones without reason.
The per-project
CLAUDE.md(and serverCONVENTIONS.md) are non-negotiable — follow them exactly. - Leave the tree green. Your phase is not done until the project's own quality gate passes (§4) and the Definition of Done (definition-of-done.md) is fully met.
- Think about the future. Every decision should consider scalability and maintenance: indexing, pagination, caching, idempotency, re-render cost, bundle size, the seam that lets a mock become real. See the conventions checklists (backend · frontend).
1. The canonical sources of truth (know where each rule lives)
| You need… | Read |
|---|---|
| Repo-wide rules, two-project layout | root CLAUDE.md |
| Backend engineering rules | server/CLAUDE.md + server/CONVENTIONS.md |
| Frontend engineering rules | client/CLAUDE.md |
| Frontend design/brand rules | the frontend-designer skill (invoke it for any UI work) |
| Product / business / data-model / payments truth | product/ (start at product/index.md) |
| The whole build plan + order | dev/phases/README.md |
| Cross-project API/flow contracts | dev/contracts/ |
| What the other agent has handed you | dev/shared-working-context/ |
Precedence when two sources seem to conflict: product docs (business truth) → the relevant
CLAUDE.md/CONVENTIONS.md (engineering truth) → this file → the phase file's specifics. If a real
conflict remains, do the safe thing, implement it, and flag it in your report — never silently guess
on money, auth, tenancy, or clinical-data rules.
2. Phase lifecycle (do these in order)
- Orient. Read this file, your phase file end-to-end, and the Required reading it lists.
Skim the prior-phase reports in
dev/shared-working-context/reports/for what changed recently. - Plan. Restate the scope to yourself; list the entities/endpoints/screens you will build and the order. Identify every external dependency you must mock and the seam it sits behind.
- Build. Implement the full scope following the project conventions and the checklists. Wire mocks behind DI. Keep commits/changes coherent.
- Self-verify. Run the project's quality gate (§4) and the phase's "How to test" steps. Fix everything. Re-read your own diff as a reviewer would.
- Document & hand off. Update the project docs (§5), write the contract(s) (§6, backend), write the phase report and update the mock registry (§7), write the handoff note (§6), and save memory (§8).
- Declare done only when the Definition of Done is fully satisfied.
3. Best-practice mandate (this is graded, not optional)
Backend (full list in backend-conventions-checklist.md):
Clean-Architecture boundaries (Domain → Application → Infrastructure → API; dependencies point inward);
CQRS via martinothamar/Mediator (ISender/ICommand/IQuery, internal sealed handlers,
OperationResult for expected failures — never throw); AsNoTracking() + Select() projection on
every read; pagination on every list; one IEntityTypeConfiguration<T> per entity; soft-delete query
filters; audit fields via the SaveChanges interceptor; caching read-heavy/config data behind the
cache seam; idempotency + (where stated) Redis distributed locks on the money path; money is IRR
BIGINT, no floats, ever; validate at the boundary with FluentValidation; CancellationToken
through every async call; zero new build warnings; no dead code; comment the why.
Frontend (full list in frontend-conventions-checklist.md):
Respect the RSC/client boundary; fetch only through clientFetch/serverFetch in
services/{domain}; use TanStack Query with sensible queryKeys, staleTime, and cache
invalidation so you never re-fetch data you already have; prevent needless re-renders (stable
references, select, colocated state, memo only where it pays); MUI primitives stay MUI (Button,
Avatar, Paper… — never re-implement a root component), but compose shareable mid-level components at
the right shared level (not buried in a page) when they'll be reused; every user-visible string is an
i18n key in both en.json and fa.json; colours from tokens.css; RTL-correct; npm run check
green; no dead code.
If a phase's work could be done quickly-but-wrong or properly-but-slower, do it properly.
4. The quality gate (run before declaring done)
- Backend:
dotnet build Baya.sln(zero new warnings) anddotnet test Baya.sln(all pass). Add the handler/integration tests your phase introduces. A reachable SQL Server is required to run. - Frontend:
npm run check(type + lint) andnpm run test:ciif you touched/added a shared component. Keepen.json/fa.jsonin sync.
A phase that doesn't pass its own gate is not done, regardless of how complete the code looks.
5. Update the project documents (in the same change)
Keep the docs honest — stale instructions are worse than none. When your phase changes how something works or what the architecture is:
- Architecture map: if you add/rename/remove a project, layer, route group, provider, domain folder,
or a cross-boundary seam, update the canonical map in the same change — server: the Project map
in
server/CLAUDE.md; client: the Project Structure tree inclient/CLAUDE.md; repo-level: the Repository layout in rootCLAUDE.md. - Product docs: if you discover or decide a business rule that the
product/docs don't yet capture (or that drifts from them), update the relevantproduct/**.md(and regenerate the HTML view perproduct/CLAUDE.mdguidance if you changed Markdown). Do not invent rules — record decisions. - Conventions: if you establish a new reusable pattern (a seam, a base class, a hook family), add a
short note to the relevant
CLAUDE.md/CONVENTIONS.mdso the next phase reuses it.
Do not re-introduce removed starter/template scaffolding while editing docs.
6. Contracts & the parallel-agent handoff (how the two sides stay in sync)
Backend and frontend phases are designed to run as two independent agents in parallel. They never edit the same files. They coordinate through two folders:
dev/contracts/— backend-owned, frontend-read. When a backend phase ships an API, it writes the contract: a per-domain doc underdev/contracts/domains/<domain>.md(request/response shapes, routes, status codes, enums, examples) and ensures the server'sswagger.jsonsnapshot is published perdev/contracts/openapi/README.md. Follow the envelope/format rules indev/contracts/conventions/. The frontend treats these as the source of truth for types — it does not guess shapes.dev/shared-working-context/— the running handoff. Strict lane ownership so parallel agents never collide:- Backend writes only:
shared-working-context/backend/STATUS.md(append your phase summary),shared-working-context/backend/handoff/after-backend-phase-N.md(one new file per phase — what the frontend can now build, which endpoints/contracts are live, what's mocked), and your report + the mock registry inshared-working-context/reports/. - Frontend writes only:
shared-working-context/frontend/STATUS.md, andshared-working-context/frontend/requests/for-backend.md(append contract gaps / shape requests — the backend agent reads this, you never edit backend files), and your frontend report inshared-working-context/reports/. - Never edit a file the other lane owns. If the frontend needs a contract change, it requests it; the backend delivers it in a subsequent change. This is what makes parallel runs safe.
- Backend writes only:
When mock data is needed on the frontend before its backend phase is merged, build it behind the same
services/{domain} seam (a mock clientApi) and record it in your frontend report so it's swapped out
cleanly once the real endpoint lands.
7. The mock / integration report (mandatory, saved to a file — not just chat)
Every phase that mocks or defers an external service must:
- Put the mock behind a DI-registered interface (a "seam") with a real-shaped contract, so the real
implementation is a drop-in later. Mock and real both implement the same interface; selection is by
configuration/registration, never by
if (mock)scattered in handlers. - Append an entry to
dev/shared-working-context/reports/mocks-registry.mdwith: the seam (interface name + file), what is faked, why, the config keys it reads, and step-by-step how to make it real (which provider, which package, which settings, which methods to implement, what to test). - Write a per-phase report at
dev/shared-working-context/reports/<backend|frontend>-phase-N-report.mdcovering: what was built, what is now testable and exactly how, what is mocked / waiting on a real service, contracts produced/consumed, follow-ups for later phases. (See reports/README.md for the template.)
Services that will be mocked across the chain and the seam each lives behind (define once, reuse):
SMS/OTP delivery (ISmsSender), object storage / MinIO/S3 (IObjectStorage), cache/Redis
(ICacheService), distributed locks/Redis (IDistributedLock), search/Elasticsearch (INurseSearch),
PSP card gateway (IPaymentProvider), settlement-sharing/تسهیم (ISettlementSplitProvider), BNPL
(IBnplProvider), bank transfer PAYA/SATNA (IBankTransferProvider), webhook verification
(IWebhookVerifier), Shahkar (IShahkarVerifier), identity KYC/liveness (IIdentityKycProvider),
credential/license check (ICredentialVerifier), IBAN ownership (IBankAccountOwnershipVerifier),
geocoding/maps (IGeocoder), مودیان e-invoicing (IMoadianClient), AI review moderation
(IReviewModerationService), field encryption/KMS (IFieldEncryptor), notification dispatch
(INotificationDispatcher). The exact phase that introduces each seam is in the roadmap; later phases
reuse the seam, they do not redefine it.
8. Write memory & context when done
After the work passes its gate, record what a future agent would need and could not cheaply re-derive:
- Persistent memory (the Claude memory dir, per the repo's memory instructions): save/update a
project-type memory note for any non-obvious decision, new seam, or gotcha this phase introduced, and add a one-line pointer toMEMORY.md. Don't record what the code/docs already make obvious. - Working context: the handoff note (§6) and the phase report (§7) — these are the durable record the other agent and the next phase rely on.
9. When you're genuinely blocked
Some things are intentionally out of scope and must be mocked, not invented: real PSP/BNPL connections, the Shahkar/MoH/INO/criminal-record vendors, MinIO/S3 credentials, the مودیان enrollment. If you reach one, implement the seam + a faithful mock + the registry entry, and move on — do not stall and do not fabricate a real integration. If a business rule is truly undecided (the product docs say "open question"), pick the safe default, implement it config-drivenly where possible, and flag it in your report for the human to confirm. Never block the chain on an external unknown.