Authoritative rules for Po{Name} .NET solutions. Deviations must be recorded in AGENT.MD with a reason.
- 1.1 Naming — Solution, projects, and root namespaces use the
Po{Name}prefix (PoWatch,PoWalker). - 1.2 Stack — .NET 10 / C# 15. Dependencies centralized in
/Directory.Packages.props. - 1.3 Compiler — Every project:
<Nullable>enable</Nullable>,<TreatWarningsAsErrors>true</TreatWarningsAsErrors>. Zero warnings. - 1.4 Performance & Trimming —
<IsTrimmable>true</IsTrimmable>and<EnableTrimAnalyzer>true</EnableTrimAnalyzer>. Standardize onSystem.Text.Jsonsource generators (JsonSerializerContext) inPo{Name}.Sharedfor zero-reflection serialization across API and WASM.- Use the
JsonTypeInfo<T>overloads at call sites, not theJsonSerializerOptionsones — the latter carryRequiresUnreferencedCodeon the method, so they fail the trim analyzer regardless of what the options contain. - A wire contract must be a concrete named type. Anonymous types cannot be source-generated, so an endpoint returning one silently forces the reflection path.
- Use the
- 1.5 Git — Trunk-based on
master. No other branches unless explicitly requested. - 1.6 Domain Integrity — No primitive obsession. Strongly-typed IDs (
readonly record struct) and enums. Zero magic strings. - 1.7 AI Provider Selection — Default to the most cost-effective model across Azure, Google, or Hugging Face that can fulfil the task.
- 2.1 Depth — Max 2 levels inside
src/.
/
├── AGENT.md
├── Directory.Packages.props
├── SCRIPTS/setup.ps1
├── src/
│ ├── Po{Name}.API/ # Minimal API, BFF host, storage, feature slices
│ ├── Po{Name}.Client/ # Blazor WASM UI
│ └── Po{Name}.Shared/ # DTOs, enums, interfaces, validation
└── tests/
├── Po{Name}.Unit/ # Pure logic, no I/O
├── Po{Name}.Integration/ # Azurite / Testcontainers
├── Po{Name}.E2EAPI/ # API contract only
└── Po{Name}.E2EUI/ # Playwright (mobile + desktop)
- 2.2 Vertical Slices
- Endpoints, request/response DTOs, and handlers live together in
Po{Name}.API/Features/{FeatureName}. - Slices never reference each other. Shared models belong strictly in
Po{Name}.Shared. - The API project hosts and serves the Blazor WASM client.
- Endpoints, request/response DTOs, and handlers live together in
- 3.1 Endpoints —
IEndpointRouteBuilder+MapGroup(). Document viaMicrosoft.AspNetCore.OpenApi+ Scalar UI. - 3.2 Diagnostics — Expose
/healthand/diag./diagmasks every secret value. - 3.3 BFF
- Zero tokens in the browser: the client talks only through
HttpOnly,SameSite=Strict, secure cookies. - Entra ID OAuth uses the
/commonendpoint with a server-sideFallbackPolicy. - Propagate
X-Session-IDandX-Correlation-IDthrough all outbound HTTP calls.
- Zero tokens in the browser: the client talks only through
- 3.4 Dev/Test Auth —
FakeAuthHandlerdriven byX-Fake-User/X-Fake-Roles. It must throwInvalidOperationExceptionif constructed in Production.
- 4.1 Layout Contract — Header: left = branding, center = actions, right = session/logout.
- 4.2 State & Security — Antiforgery validation on every state-changing Minimal API endpoint (POST/PUT/DELETE/PATCH). Enforce state isolation in Blazor WASM with explicit
IDisposable/IAsyncDisposablecleanups so nothing leaks across sessions.app.UseAntiforgery()alone does not satisfy this: it validates only endpoints whose metadata requests it, which the framework adds just for form-binding endpoints. A JSON API needs middleware that validates unsafe methods by default and requires an explicit, reasoned opt-out.
- 4.3 State Visibility — Persistent "USING MOCK DATA" banner whenever local mock data is active. (Deprecated — no mock-data plumbing is wired in this app.)
- 4.4 Components — Radzen Blazor is the primary UI library; prefer its advanced interactive components over hand-rolled equivalents. Before building a control, check whether Radzen already ships it —
RadzenMediaQuery,RadzenBreadCrumbandRadzenSplitterin particular replace custom JS interop and markup. - 4.5 Styles — Inline styles forbidden. Scoped CSS (
.razor.css) +:rootcustom properties for design tokens. Light/dark themes follow the system setting dynamically.color-schememust track the app's own theme attribute, not just the OS, or UA-rendered surfaces (scrollbars, form controls,<progress>) disagree with the page.- Prefer
@containerover@mediafor anything sized by the content well rather than the viewport — a sidebar changes the well's width without the viewport moving. - Status and series colours belong in theme-aware tokens. A hardcoded hex cannot pass contrast in both themes.
- 4.6 Performance —
Virtualizefor long lists. WebGL/Canvas for heavy visuals. - 4.7 Accessibility — WCAG 2.2 AA on every interactive element, including measured text contrast (4.5:1 body, 3:1 large text and UI components).
- 5.1 Local AI — Model registries with dtype fallback chains for browser/worker-native execution.
- 5.2 AI Test Interception — A custom
DelegatingHandlerintercepts Azure AI pipeline calls in test environments so no tokens are consumed. - 5.3 Logging —
[LoggerMessage]source generators on high-frequency paths. No string interpolation in logs. - 5.4 Resilience & Cache — .NET 10
AddResiliencePipelineandHybridCachefor all HTTP resilience and caching.
- 6.1 Test Counts — 100 Unit | 50 Integration | 25 API E2E | 25 UI E2E.
- 6.2 Azure — Resources in resource group
PoShared(orPo{SolutionName}). Auth via System-Assigned Managed Identity + Key Vault. No raw connection strings in app settings. - 6.3 Post-Deploy Smoke Test — CI verifies, after deploy: Blazor render-tree initialization,
/healthreturns healthy,/diagreturns masked config safely. - 6.4 Hygiene — Continuously purge dead code and orphaned assets.
AGENT.MDis the living architectural source of truth.