diff --git a/docs/CONTRACT_TESTING_QUICKSTART.md b/docs/CONTRACT_TESTING_QUICKSTART.md
deleted file mode 100644
index a03224bf4..000000000
--- a/docs/CONTRACT_TESTING_QUICKSTART.md
+++ /dev/null
@@ -1,168 +0,0 @@
-# Contract Testing Quick Start Guide
-
-## π Quick Setup
-
-### 1. Run the Full Contract Test Suite
-```bash
-npm run test:contracts
-```
-
-This single command will:
-- β
Validate API contracts
-- β
Generate OpenAPI spec
-- β
Generate TypeScript types
-- β
Check for breaking changes
-- β
Verify website type compatibility
-
-### 2. Individual Commands
-
-```bash
-# Validate API contracts only
-npm run test:api:contracts
-
-# Generate types (after making DTO changes)
-npm run api:sync-types
-
-# Check compatibility (detect breaking changes)
-npm run test:contract:compatibility
-
-# Verify website can consume types
-npm run website:type-check
-```
-
-## π What Gets Created
-
-### Generated Types
-- **Location**: `apps/website/lib/types/generated/`
-- **Files**: One `.ts` file per DTO (e.g., `RaceDTO.ts`, `DriverDTO.ts`)
-- **Usage**: Import directly in website code
-
-### Test Files
-- **API Tests**: `apps/api/src/shared/testing/contractValidation.test.ts`
-- **Website Tests**: `apps/website/lib/types/contractConsumption.test.ts`
-
-### CI/CD
-- **Workflow**: `.github/workflows/contract-testing.yml`
-- **Triggers**: Pull requests and main branch pushes
-
-## π― Common Workflows
-
-### Making API Changes
-
-1. **Update DTO in API**:
- ```typescript
- // apps/api/src/domain/race/dtos/RaceDTO.ts
- export class RaceDTO {
- @ApiProperty()
- id: string;
-
- @ApiProperty()
- name: string;
-
- @ApiProperty({ required: false })
- description?: string; // New optional field
- }
- ```
-
-2. **Run contract tests**:
- ```bash
- npm run test:contracts
- ```
-
-3. **If tests pass**, commit your changes. The CI will regenerate types automatically.
-
-### Updating Website Code
-
-1. **Import generated types**:
- ```typescript
- import type { RaceDTO } from '@/lib/types/generated/RaceDTO';
-
- function RaceComponent({ race }: { race: RaceDTO }) {
- return
{race.name}
;
- }
- ```
-
-2. **TypeScript will catch errors** if contracts change.
-
-### Detecting Breaking Changes
-
-```bash
-# This will show you exactly what changed
-npm run test:contract:compatibility
-```
-
-Output example:
-```
-π¨ BREAKING CHANGES DETECTED:
- β’ RaceDTO.status: Property status was removed (BREAKING)
-
-β REMOVED:
- β’ OldDTO: DTO OldDTO was removed
-
-β ADDED:
- β’ NewDTO: New DTO NewDTO was added
-```
-
-## π§ Troubleshooting
-
-### "Cannot find module" errors
-```bash
-# Regenerate types
-npm run api:sync-types
-```
-
-### Type generation fails
-1. Check DTOs have `@ApiProperty` decorators
-2. Verify OpenAPI spec is valid: `cat apps/api/openapi.json`
-3. Run individual steps:
- ```bash
- npm run api:generate-spec
- npm run api:generate-types
- ```
-
-### CI fails on breaking changes
-- Review what changed
-- Update website code to handle new types
-- Or revert the breaking change if unintended
-
-## π Checklist Before Committing
-
-- [ ] Run `npm run test:contracts` locally
-- [ ] All tests pass
-- [ ] No breaking changes (or they're intentional)
-- [ ] Website code updated to handle new types
-- [ ] Generated types are committed (if needed)
-
-## π Key Concepts
-
-### What is a "Contract"?
-A contract is the agreement between API and website about what data looks like:
-- DTO definitions
-- Property types
-- Required vs optional fields
-
-### What are "Breaking Changes"?
-Changes that would break the website:
-- Removing required fields
-- Changing field types
-- Adding required fields to existing DTOs
-
-### Why Generate Types?
-- **Type Safety**: Catch errors at compile time
-- **Auto-completion**: Better IDE experience
-- **Documentation**: Types serve as living documentation
-- **Consistency**: Single source of truth
-
-## π¨ Important Notes
-
-1. **Never manually edit generated files** - they're auto-generated
-2. **Always run tests before committing** - prevents breaking changes
-3. **The CI will regenerate types** - but local verification is faster
-4. **Breaking changes need review** - consider versioning strategy
-
-## π More Resources
-
-- Full documentation: `docs/CONTRACT_TESTING.md`
-- API examples: `apps/api/src/shared/testing/contractValidation.test.ts`
-- Website examples: `apps/website/lib/types/contractConsumption.test.ts`
-- CI/CD workflow: `.github/workflows/contract-testing.yml`
\ No newline at end of file
diff --git a/docs/architecture/website/CLIENT_STATE.md b/docs/architecture/website/CLIENT_STATE.md
new file mode 100644
index 000000000..5b2ccbc38
--- /dev/null
+++ b/docs/architecture/website/CLIENT_STATE.md
@@ -0,0 +1,60 @@
+# Client State (Strict)
+
+This document defines the only allowed usage of client state in `apps/website`.
+
+Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
+
+## 1) Core rule
+
+Client state is allowed only for **UI concerns**.
+
+The API remains the single source of truth.
+
+## 2) Allowed client state
+
+Client state MAY represent:
+
+- selection (table rows, active tab)
+- open/closed dialogs and drawers
+- input values before submission
+- loading flags
+- best-effort optimistic flags
+
+## 3) Forbidden client state
+
+Client state MUST NOT be used as:
+
+- business truth
+- security truth
+- permission truth
+
+Examples of forbidden behavior:
+
+- client code deciding that a user is allowed based on local flags
+- client code persisting an authoritative list state that overrides server truth
+
+## 4) Conflict resolution rule (hard)
+
+If client state and API truth disagree, **API truth wins**.
+
+Correct handling is:
+
+- show the API result
+- revalidate and reload server-rendered truth
+
+## 5) Relationship to Blockers
+
+Blockers exist to prevent UX mistakes.
+
+- Blockers are not security.
+- Blockers may reduce unnecessary requests.
+- The API still enforces rules.
+
+See [`BLOCKER_GUARDS.md`](docs/architecture/website/BLOCKER_GUARDS.md:1).
+
+## 6) Canonical placement in this repo
+
+- `apps/website/lib/blockers/**`
+- `apps/website/lib/hooks/**`
+- `apps/website/lib/command-models/**`
+
diff --git a/docs/architecture/website/DISPLAY_OBJECTS.md b/docs/architecture/website/DISPLAY_OBJECTS.md
index 65b0b1a87..e470c43d1 100644
--- a/docs/architecture/website/DISPLAY_OBJECTS.md
+++ b/docs/architecture/website/DISPLAY_OBJECTS.md
@@ -25,7 +25,7 @@ They exist to avoid duplicating presentation logic across View Models.
A Display Object MAY:
- format values (money, dates, durations)
-- handle localization and language-specific rules
+- handle localization only when localization inputs are deterministic (for example: mapping stable codes to stable labels)
- map codes to labels
- encapsulate UI display conventions
- be reused across multiple View Models
@@ -67,6 +67,20 @@ In this repository, a Display Object MUST NOT:
Reason: these are runtime-locale/timezone dependent and cause SSR/hydration mismatches.
+## Localization rule (strict)
+
+Localization MUST NOT depend on runtime locale APIs.
+
+Allowed approaches:
+
+- API returns the exact labels/strings for the current user context.
+- Website maps stable codes to stable labels using a deterministic table.
+
+Forbidden approaches:
+
+- any usage of `Intl.*`
+- any usage of `toLocale*`
+
If a rule affects system correctness or persistence,
it does not belong in a Display Object.
diff --git a/docs/architecture/website/FORM_SUBMISSION.md b/docs/architecture/website/FORM_SUBMISSION.md
index 6bd3d4694..d37ce2f3d 100644
--- a/docs/architecture/website/FORM_SUBMISSION.md
+++ b/docs/architecture/website/FORM_SUBMISSION.md
@@ -5,6 +5,8 @@ It applies to all write operations (create, update, delete).
There are no exceptions.
+Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
+
βΈ»
Core Principle
@@ -13,6 +15,21 @@ Read and Write paths are different.
What is displayed is never sent back.
+## Non-negotiable write boundary
+
+All writes MUST enter the system through **Next.js Server Actions**.
+
+Forbidden:
+
+- client components performing write HTTP requests
+- client components calling API clients for mutations
+
+Allowed:
+
+- client submits intent (FormData, button action)
+- server action performs UX validation
+- server action calls the API
+
βΈ»
High-Level Flow
@@ -153,4 +170,4 @@ Summary
β’ Read Flow: DTO β ViewModel β UI
β’ Write Flow: UI β Command DTO β Core
-What is shown is never sent back.
\ No newline at end of file
+What is shown is never sent back.
diff --git a/docs/architecture/website/PRESENTERS.md b/docs/architecture/website/PRESENTERS.md
new file mode 100644
index 000000000..cf52d6443
--- /dev/null
+++ b/docs/architecture/website/PRESENTERS.md
@@ -0,0 +1,56 @@
+# Presenters (Strict)
+
+This document defines the **Presenter** boundary for `apps/website`.
+
+Presenters exist to prevent responsibility drift into:
+
+- server routes
+- Page Queries
+- Templates
+
+## 1) Definition
+
+A **Presenter** is a deterministic, side-effect free transformation between website presentation models.
+
+Allowed transformations:
+
+- Page DTO β ViewData
+- Page DTO β ViewModel
+- ViewModel β ViewData
+
+## 2) Non-negotiable rules
+
+1. Presenters MUST be deterministic.
+2. Presenters MUST be side-effect free.
+3. Presenters MUST NOT perform HTTP.
+4. Presenters MUST NOT call API clients.
+5. Presenters MUST NOT access cookies/headers.
+6. Presenters MAY use Display Objects.
+7. Presenters MUST NOT import Templates.
+
+## 3) Where Presenters run
+
+Presenters run in **client code only**.
+
+Presenters MUST be defined in `'use client'` modules.
+
+If a computation affects routing decisions (redirect, notFound), it belongs in a Page Query or server route composition, not in a Presenter.
+
+## 4) Relationship to Display Objects
+
+Display Objects implement reusable formatting/mapping.
+
+Rules:
+
+- Presenters may orchestrate Display Objects.
+- Display Object instances MUST NOT appear in ViewData.
+
+See [`DISPLAY_OBJECTS.md`](docs/architecture/website/DISPLAY_OBJECTS.md:1) and [`VIEW_DATA.md`](docs/architecture/website/VIEW_DATA.md:1).
+
+## 5) Canonical placement in this repo (strict)
+
+Presenters MUST live colocated with ViewModels under:
+
+- `apps/website/lib/view-models/**`
+
+Reason: this repo already treats `apps/website/lib/view-models/**` as the client-only presentation module boundary.
diff --git a/docs/architecture/website/VIEW_DATA.md b/docs/architecture/website/VIEW_DATA.md
index 00281c744..f08e27549 100644
--- a/docs/architecture/website/VIEW_DATA.md
+++ b/docs/architecture/website/VIEW_DATA.md
@@ -2,6 +2,8 @@
ViewData is the **only** allowed input type for Templates in `apps/website`.
+Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
+
## 1) Definition
ViewData is a JSON-serializable, template-ready data structure:
@@ -14,7 +16,7 @@ ViewData is a JSON-serializable, template-ready data structure:
ViewData is not:
-- a Page DTO (raw transport)
+- an API Transport DTO (raw transport)
- a ViewModel (client-only class)
- a Display Object instance
@@ -25,8 +27,13 @@ ViewData MUST be created in client code:
1) Initial SSR-safe render: `ViewData = fromDTO(PageDTO)`
2) Post-hydration render: `ViewData = fromViewModel(ViewModel)`
+These transformations are Presenters.
+See [`PRESENTERS.md`](docs/architecture/website/PRESENTERS.md:1).
+
Templates MUST NOT compute derived values.
+Presenters MUST NOT call the API.
+
## 4) Determinism rules
Any formatting used to produce ViewData MUST be deterministic.
@@ -38,9 +45,11 @@ Forbidden anywhere in formatting code paths:
Reason: SSR and browser outputs can differ.
+Localization MUST NOT depend on runtime locale APIs.
+If localized strings are required, they MUST be provided as deterministic inputs (for example via API-provided labels or a deterministic code-to-label map) and passed through Presenters into ViewData.
+
## 5) Relationship to Display Objects
Display Objects are used to implement formatting/mapping, but their instances MUST NOT be stored inside ViewData.
Only primitive outputs produced by Display Objects may appear in ViewData.
-
diff --git a/docs/architecture/website/WEBSITE_CONTRACT.md b/docs/architecture/website/WEBSITE_CONTRACT.md
new file mode 100644
index 000000000..d67af84fa
--- /dev/null
+++ b/docs/architecture/website/WEBSITE_CONTRACT.md
@@ -0,0 +1,253 @@
+# Website Architecture Contract (Strict)
+
+This document is the **authoritative contract** for `apps/website`.
+
+If any other website document conflicts with this one, **this one wins**.
+
+## 1) Purpose (non-negotiable)
+
+The website is a **delivery layer**.
+
+It does **not**:
+
+- contain business rules
+- make authorization decisions
+- own or persist business truth
+
+It **only**:
+
+- renders truth from `apps/api`
+- collects user intent
+- forwards user intent to `apps/api`
+
+The API is the single source of truth.
+
+## 2) System context (hard boundary)
+
+The website never bypasses the API.
+
+```text
+Browser
+ β
+Next.js App Router (RSC + Server Actions)
+ β
+HTTP
+ β
+Backend API (Use Cases, Domain, Database)
+```
+
+## 3) Website presentation model types (strict)
+
+### 3.1 API Transport DTO
+
+Definition: the shape returned by the backend API over HTTP.
+
+Rules:
+
+- API Transport DTOs MUST be contained inside infrastructure.
+- API Transport DTOs MUST NOT be imported by Templates.
+
+Canonical placement in this repo:
+
+- `apps/website/lib/types/**` (transport DTOs consumed by services and page queries)
+
+### 3.2 Page DTO
+
+Definition: the website-owned, server-to-client payload for a route.
+
+Rules:
+
+- JSON-serializable only.
+- Contains **raw** values only (IDs, ISO strings, numbers, codes).
+- MUST NOT contain class instances.
+- Created by Page Queries.
+- Passed from server routes into client code.
+
+Canonical placement in this repo:
+
+- `apps/website/lib/page-queries/**` (composition and Page DTO construction)
+
+### 3.3 ViewModel
+
+Definition: the client-only, UI-owned class representing fully prepared UI state.
+
+Rules:
+
+- Instantiated only in `'use client'` modules.
+- Never serialized.
+- MUST NOT be passed into Templates.
+
+See [`VIEW_MODELS.md`](docs/architecture/website/VIEW_MODELS.md:1).
+
+Canonical placement in this repo:
+
+- `apps/website/lib/view-models/**`
+
+### 3.4 ViewData
+
+Definition: the only allowed input type for Templates.
+
+Rules:
+
+- JSON-serializable only.
+- Contains only template-ready values (mostly strings/numbers/booleans).
+- MUST NOT contain class instances.
+
+See [`VIEW_DATA.md`](docs/architecture/website/VIEW_DATA.md:1).
+
+Canonical placement in this repo:
+
+- `apps/website/templates/**` (Templates that accept ViewData only)
+
+## 4) Presentation helpers (strict)
+
+### 4.1 Presenter
+
+Definition: a deterministic, side-effect free transformation.
+
+Presenters map between website presentation models:
+
+- Page DTO β ViewData
+- Page DTO β ViewModel
+- ViewModel β ViewData
+
+Rules:
+
+- MUST be deterministic.
+- MUST be side-effect free.
+- MUST NOT call HTTP.
+- MUST NOT call the API.
+- MAY use Display Objects.
+
+See [`PRESENTERS.md`](docs/architecture/website/PRESENTERS.md:1).
+
+Canonical placement in this repo:
+
+- colocated with ViewModels in `apps/website/lib/view-models/**`
+
+### 4.2 Display Object
+
+Definition: deterministic, reusable, UI-only formatting/mapping logic.
+
+Rules:
+
+- Class-based, immutable, deterministic.
+- MUST NOT call `Intl.*`.
+- MUST NOT call any `toLocale*`.
+- MUST NOT implement business rules.
+
+See [`DISPLAY_OBJECTS.md`](docs/architecture/website/DISPLAY_OBJECTS.md:1).
+
+Canonical placement in this repo:
+
+- `apps/website/lib/display-objects/**`
+
+## 5) Read flow (strict)
+
+```text
+RSC page.tsx
+ β
+PageQuery (server)
+ β
+API service / API client (infra)
+ β
+API Transport DTO
+ β
+Page DTO
+ β
+Presenter (client)
+ β
+ViewModel (optional, client)
+ β
+Presenter (client)
+ β
+ViewData
+ β
+Template
+```
+
+## 6) Write flow (strict)
+
+All writes MUST enter through **Next.js Server Actions**.
+
+Forbidden:
+
+- client components performing write HTTP requests
+- client components calling API clients for mutations
+
+Allowed:
+
+- client submits intent (FormData, button action)
+- server action performs UX validation
+- server action calls the API
+
+See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1).
+
+## 7) Authorization (strict)
+
+- The website may hide/disable UI for UX.
+- The website MUST NOT enforce security.
+- The API enforces authentication and authorization.
+
+See [`BLOCKER_GUARDS.md`](docs/architecture/website/BLOCKER_GUARDS.md:1).
+
+## 7.1) Client state (strict)
+
+Client-side state is allowed only for UI concerns.
+
+Allowed:
+
+- selection
+- open/closed dialogs
+- transient form state
+- optimistic flags and loading spinners
+
+Forbidden:
+
+- treating client state as business truth
+- using client state as an authorization decision
+- persisting client state as the source of truth
+
+Hard rule:
+
+- any truth returned by the API MUST overwrite client assumptions.
+
+Canonical placement in this repo:
+
+- `apps/website/lib/blockers/**` for UX-only prevention helpers
+- `apps/website/lib/hooks/**` for React-only utilities
+- `apps/website/lib/command-models/**` for transient form models
+
+See [`CLIENT_STATE.md`](docs/architecture/website/CLIENT_STATE.md:1).
+
+## 8) DI contract (Inversify) (strict)
+
+The DI system under [`apps/website/lib/di/index.ts`](apps/website/lib/di/index.ts:1) is **client-first**.
+
+Server execution is concurrent. Any shared singleton container can leak cross-request state.
+
+Rules:
+
+1. Server `app/**/page.tsx` MUST NOT access the container.
+2. Page Queries SHOULD prefer manual wiring.
+3. Client modules MAY use DI via [`ContainerProvider`](apps/website/lib/di/index.ts:11) and hooks.
+4. [`ContainerManager.getContainer()`](apps/website/lib/di/container.ts:74) is **client-only**.
+5. Any server DI usage MUST be request-scoped (a fresh container per request).
+
+Hard constraint:
+
+- A singleton Inversify container MUST NOT be used to serve concurrent server requests.
+
+See [`WEBSITE_DI_RULES.md`](docs/architecture/website/WEBSITE_DI_RULES.md:1).
+
+## 9) Non-negotiable rules (final)
+
+1. The API is the brain.
+2. The website is a terminal.
+3. API Transport DTOs never reach Templates.
+4. ViewModels never go to the API.
+5. Templates accept ViewData only.
+6. Page Queries do not format; they only compose.
+7. Presenters are pure and deterministic.
+8. Server Actions are the only write entry point.
+9. Authorization always belongs to the API.
diff --git a/docs/architecture/website/WEBSITE_DI_RULES.md b/docs/architecture/website/WEBSITE_DI_RULES.md
index 849e6bb5a..5ecfeaecd 100644
--- a/docs/architecture/website/WEBSITE_DI_RULES.md
+++ b/docs/architecture/website/WEBSITE_DI_RULES.md
@@ -2,6 +2,8 @@
This repo uses Inversify DI under [apps/website/lib/di](apps/website/lib/di/index.ts:1).
+Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
+
## 1) Non-negotiable safety rule
No stateful service instances may be shared across requests.
@@ -20,6 +22,8 @@ Reason: Next.js server execution is concurrent; shared state causes cross-reques
- SHOULD prefer explicit construction (manual wiring).
- MAY use DI only if all resolved services are stateless and safe for concurrent requests.
+If DI is used on the server, it MUST be request-scoped (a new container per request) and MUST NOT reuse a shared singleton container.
+
### 2.3 Client modules
- MAY use DI via `ContainerProvider` and hooks (example: `useInject`).
@@ -28,3 +32,7 @@ Reason: Next.js server execution is concurrent; shared state causes cross-reques
[`ContainerManager`](apps/website/lib/di/container.ts:61) holds a singleton container. Treat it as **unsafe for server request scope** unless proven otherwise.
+Strict rule:
+
+- [`ContainerManager.getContainer()`](apps/website/lib/di/container.ts:74) is client-only.
+
diff --git a/docs/architecture/website/WEBSITE_GUARDRAILS.md b/docs/architecture/website/WEBSITE_GUARDRAILS.md
index 126bf25b5..9c512cb86 100644
--- a/docs/architecture/website/WEBSITE_GUARDRAILS.md
+++ b/docs/architecture/website/WEBSITE_GUARDRAILS.md
@@ -2,11 +2,14 @@
This document defines architecture guardrails that must be enforced via tests + ESLint.
+Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
+
## 1) RSC boundary guardrails
Fail CI if any `apps/website/app/**/page.tsx`:
- imports from `apps/website/lib/view-models/*`
+- imports from Presenter code (presenters live colocated with ViewModels)
- calls `Intl.*` or `toLocale*`
- performs sorting/filtering (`sort`, `filter`, `reduce`) beyond trivial null checks
@@ -15,6 +18,7 @@ Fail CI if any `apps/website/app/**/page.tsx`:
Fail CI if any `apps/website/templates/**`:
- imports from `apps/website/lib/view-models/*`
+- imports from presenter code (presenters live colocated with ViewModels)
- imports from `apps/website/lib/display-objects/*`
- calls `Intl.*` or `toLocale*`
@@ -27,4 +31,3 @@ Fail CI if any `apps/website/lib/display-objects/**`:
- calls `Intl.*` or `toLocale*`
Display Objects must be deterministic.
-
diff --git a/docs/architecture/website/WEBSITE_RSC_PRESENTATION.md b/docs/architecture/website/WEBSITE_RSC_PRESENTATION.md
index 0cd661ec6..3153b85d0 100644
--- a/docs/architecture/website/WEBSITE_RSC_PRESENTATION.md
+++ b/docs/architecture/website/WEBSITE_RSC_PRESENTATION.md
@@ -4,6 +4,8 @@ This document defines the only allowed presentation architecture for `apps/websi
It is **website-only** and does not change `apps/api` or `core` architecture.
+Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
+
## 1) Core rule: API owns business truth
- `apps/api` is the only source of truth for business rules and canonical filtering/sorting.
@@ -42,6 +44,9 @@ It is **website-only** and does not change `apps/api` or `core` architecture.
- Contains only values ready to render (mostly strings/numbers).
- Built from Page DTO (initial render) and from ViewModel (post-hydration).
+The mapping between Page DTO, ViewModel, and ViewData is performed by Presenters.
+See [`PRESENTERS.md`](docs/architecture/website/PRESENTERS.md:1).
+
## 3) Required per-route structure
Every route MUST follow:
@@ -50,6 +55,9 @@ Every route MUST follow:
2) `*PageClient.tsx` (client): builds ViewData and renders Template
3) `*Template.tsx` (pure UI): renders ViewData only
+All writes enter through Server Actions.
+See [`FORM_SUBMISSION.md`](docs/architecture/website/FORM_SUBMISSION.md:1).
+
## 4) Authoritative specification
This document is an entry point only.
@@ -58,3 +66,6 @@ The authoritative, test-enforced spec lives at:
- [plans/nextjs-rsc-viewmodels-concept.md](plans/nextjs-rsc-viewmodels-concept.md:1)
+Final contract:
+
+- [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1)