docs(adr): land ADR-C-001, ADR-C-003, ADR-C-007 and ADR-C-009
The architect approved the four ADR-fix tickets. All four change what the architecture documents claim. No code changes. ADR-0001, ADR-C-001: the worked example claimed the POC has no real backend. It rewrites against `backend/src/BigRegister.Api`. Every path it named is repointed. The out-of-scope list drops two discharged bullets: 33 `parse*` boundaries exist, and `npm run gen:api` is real. ADR-0001, ADR-C-003: a new section states that the generated client is the wire contract. A hand-written `contracts/*.dto.ts` is the exception for two cases only. The four survivors stay, because NSwag emits every property as optional and flattens `RegistrationStatusDto` into five optional strings. The `parse*` trust boundary stays mandatory, because a generated type is a compile-time claim about the wire and not a runtime guarantee. ADR-0003, ADR-C-007: four paths moved in WP-67 and are repointed. Point 4 kept the principle and changed its example to `skeleton` and `spinner`. Two of its claims were false and the amendment says so: `app-alert` wraps the vendored `.feedback` classes, and `site-header` composes the vendored `.titlebar`. ADR-0004, ADR-C-009: the exception section states a four-part test instead of one named exception. `OrgTemplateStore` and `FeatureFlagStore` both pass it. RB-07 gated this ticket, because clause 4 needs an audited allow path. RB-07 landed that, so the ADR does not ratify a control that the code lacks. Three tickets need a matching CLAUDE.md correction in the same diff. CLAUDE.md section 2 loses the false `alert` example. Section 4 gets the generated-client rule and the four-part test. Two findings were wrong. ADR-C-001 asked to keep an out-of-scope bullet that reads "SessionStore is in-memory". The session persists to `localStorage` now, so the bullet covers multi-tab sync only. ADR-C-007 flagged one half of point 4 and missed that the other half is equally false. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -124,8 +124,10 @@ than hardcoding one app's content — the two apps' primary nav genuinely differ
|
||||
should be **composition of existing blocks** — adding building blocks is the
|
||||
exception, not the default. Atoms are thin wrappers over CIBG Huisstijl (Bootstrap 5.2)
|
||||
CSS classes (`btn`, `form-control`, `card`, …); we own only a small typed `input()` API,
|
||||
the design system does the visuals. (Where CIBG lacks a class — e.g. `alert` — the atom is a
|
||||
small hand-rolled surface built from the token bridge; see ADR-0003.)
|
||||
the design system does the visuals. (Where CIBG lacks a class — e.g. `skeleton`,
|
||||
`spinner` — the atom is a small hand-rolled surface built from the token bridge and carries a
|
||||
`// CIBG-GAP EXTENSION:` marker; see ADR-0003. `alert` is **not** such a case: it wraps the
|
||||
vendored `.feedback feedback-*` classes.)
|
||||
|
||||
### 3. State: make illegal states unrepresentable
|
||||
|
||||
@@ -175,8 +177,14 @@ herregistratie eligibility) or _config value_ (server sends threshold, FE applie
|
||||
for instant feedback, server re-validates as authority — e.g. scholing threshold).
|
||||
FE keeps only **format** validation, never as authority.
|
||||
|
||||
DTO lives in `contracts/`; a hand-written `parse*`/`toDomain` in `infrastructure/`
|
||||
validates the untrusted shape and maps DTO → domain. Wiring a real .NET backend
|
||||
The generated client
|
||||
(`libs/shared/src/infrastructure/api-client.ts`, `npm run gen:api`, drift-checked in CI) **is**
|
||||
the wire contract — consume its types directly, as 19 of the 20 adapters do. A hand-written
|
||||
`contracts/*.dto.ts` is the exception, only where codegen does not reach the endpoint or types
|
||||
it too loosely (the four survivors are all the latter — the generator emits every property as
|
||||
optional and flattens unions); such a file must still import nothing. Either way a hand-written
|
||||
`parse*`/`toDomain` in `infrastructure/` validates the untrusted shape and maps DTO → domain —
|
||||
**a generated type is a compile-time claim about the wire, not a runtime guarantee.** Wiring a real .NET backend
|
||||
touches only `infrastructure/` + `contracts/` (see ARCHITECTURE §6). Server-owned
|
||||
rules live **only** on the server, with no FE mirror to drift from it — the FE may
|
||||
mirror a server-supplied _value_ (a threshold, a bound) for instant feedback, but
|
||||
@@ -185,8 +193,12 @@ never reimplements the _algorithm_.
|
||||
**Business-tunable reference data ("stamdata") is config-as-code, not a DB.** Tables the
|
||||
business controls (profession↔diploma map, thresholds, policy-question text) live as typed
|
||||
C# in `backend/.../Stamdata/`, validated at build by `StamdataValidationTests` (a bad edit
|
||||
fails CI, never prod) — never runtime-editable. Org-templates are the deliberate exception
|
||||
(operational per-org config in SQLite). UI copy is `$localize`. See ADR-0004.
|
||||
fails CI, never prod) — never runtime-editable. Operational configuration is the deliberate
|
||||
exception, and ADR-0004 states it as a four-part test rather than a list: the catalog lives in
|
||||
code, an unknown key fails closed, the value is operational rather than a shared business rule,
|
||||
and writes are admin-capability-gated **and** audited. Two surfaces pass it today —
|
||||
`OrgTemplateStore` (per-org letterhead) and `FeatureFlagStore` (rollout switches), both in
|
||||
SQLite. A third surface must pass the same test, not argue by analogy. UI copy is `$localize`. See ADR-0004.
|
||||
|
||||
### 5. Testing
|
||||
|
||||
|
||||
Reference in New Issue
Block a user