docs: archive the finished backlogs (RD-30)
Two backlog trees are complete: `docs/project/backlog/` (75 files, every WP done) and `docs/project/refactor-backlog-setup/` (the arc before it). Move both under `docs/project/archive/` with `git mv`, so history stays intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them, because it points at the now-archived backlog README. Add `docs/project/archive/README.md`. It states that these trees are historical and names the two directories that are still live. Repoint every inbound reference named in RD-30's Files table: CLAUDE.md, the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the `document-feature` and `new-ssp` skills, and the readable-codebase PLAN, README, and RD-19 ticket. Fix two upward-relative links inside the moved WP files (WP-68, WP-69) that gained a directory level and would otherwise break. Repoint `.prettierignore`'s two agent-prompt exclusions to their new path, so prettier keeps leaving those files' exact wording alone. Mark RD-30 done and check off its acceptance criteria; flip its README row to done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
||||
# ADR-C-003 — state that the generated client is the wire contract
|
||||
|
||||
Status: **implemented** · 2026-08-27 · Source finding: `06-adr-conformance.md` §ADR-C-003
|
||||
|
||||
## What was wrong
|
||||
|
||||
ADR-0001 set "one source of truth that generates types for both sides" as the target state.
|
||||
The code reached it. CLAUDE.md §4 still stated the pre-codegen rule — _"DTO lives in
|
||||
`contracts/`"_ — as standing law, so §4 could be cited to justify both deleting the four
|
||||
survivors and adding new hand-written DTOs for already-generated endpoints.
|
||||
|
||||
## What changed
|
||||
|
||||
| File | Change |
|
||||
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| `docs/reference/architecture/0001-...md` | **new** §"Where the contract lives, after codegen" |
|
||||
| `CLAUDE.md` §4 | the flat "DTO lives in `contracts/`" rule replaced with the generated-client rule + the two exceptions |
|
||||
|
||||
No code changed. Per CLAUDE.md's own precedence rule, the ADR was amended first and
|
||||
CLAUDE.md corrected to match, in one diff.
|
||||
|
||||
## The decision the finding asked for: the four survivors stay
|
||||
|
||||
ADR-C-003 required an explicit, recorded decision on the four remaining hand-written DTOs.
|
||||
**They stay**, all four under exception case 2 ("the generator types the shape too loosely").
|
||||
This is not a preference — adopting the generated shapes would violate CLAUDE.md §3.
|
||||
|
||||
Evidence. NSwag emits every property as optional, and flattens a discriminated union into a
|
||||
bag of optional fields:
|
||||
|
||||
| | generated (`api-client.ts`) | hand-written (`dashboard-view.dto.ts`) |
|
||||
| ----------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| `DashboardViewDto` | `registration?`, `person?`, `decisions?` — all optional (`:2017`) | all three required |
|
||||
| `RegistrationDto` | six optional fields (`:2202`) | six required fields |
|
||||
| `RegistrationStatusDto` | **one flat record of five optional strings**, `tag?: string` (`:2211`) | a real union of three variants, `tag: 'Geregistreerd' \| 'Geschorst' \| 'Doorgehaald'`, per-variant fields required |
|
||||
|
||||
The generated `RegistrationStatusDto` makes `{ tag: 'Geregistreerd', doorgehaaldOp: '…' }`
|
||||
representable. That is precisely the illegal state CLAUDE.md §3 exists to forbid, and the
|
||||
`parse*` boundary would have to reconstruct the union by hand anyway.
|
||||
|
||||
The ADR therefore records that retiring these four is **not** a cleanup to schedule. It
|
||||
becomes correct only if the backend annotates its DTOs so the generator emits required
|
||||
properties and real unions — which names the actual prerequisite instead of leaving the
|
||||
question open.
|
||||
|
||||
## Verified counts, not carried over from the finding
|
||||
|
||||
- Hand-written `contracts/*.dto.ts`: **4** —
|
||||
`apps/ssp/src/app/registratie/contracts/{brp-address,dashboard-view,duo-diplomas}.dto.ts`
|
||||
and `libs/beheer/src/contracts/stamdata.dto.ts`.
|
||||
- All four duplicate generated types **by the same names**: `BrpAddressDto` (`:1997`),
|
||||
`DashboardViewDto` (`:2017`), `DuoLookupDto` (`:2056`), `DuoDiplomaDto` (`:2047`),
|
||||
`PolicyQuestionDto` (`:2168`), `ManualDiplomaPolicyDto` (`:2106`), `StamdataColumnDto`
|
||||
(`:2246`), `StamdataTableDto` (`:2253`), `StamdataTableSummaryDto` (`:2261`). None is a
|
||||
codegen gap — the finding's "case 1" has no occupant today, which is worth knowing.
|
||||
- The `parse*` boundary is restated as mandatory regardless of type provenance. The amendment
|
||||
says why in one line: a generated type is a compile-time claim about the wire, not a
|
||||
runtime guarantee.
|
||||
|
||||
## Gate released
|
||||
|
||||
ADR-C-003 blocked any ticket that would delete the four `contracts/*.dto.ts` files or add a
|
||||
hand-written DTO for a generated endpoint. No open ticket needed it. The rule is now written
|
||||
down, so a future one can be judged against it rather than against a stale §4.
|
||||
Reference in New Issue
Block a user