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>
67 lines
2.6 KiB
Markdown
67 lines
2.6 KiB
Markdown
# WP-05 — Parse-don't-validate closure + MDX
|
|
|
|
Status: done
|
|
Phase: 1 — FP/DDD core
|
|
|
|
## Why
|
|
|
|
Three adapters trust the wire, contradicting the repo's flagship "parse, don't validate"
|
|
principle (every response through a hand-written `parse*` returning `Result`).
|
|
|
|
## Read first
|
|
|
|
- `CLAUDE.md` §3 + §4; `docs/reference/architecture/0001-bff-lite-decision-dtos.md`
|
|
- `src/app/registratie/infrastructure/applications.adapter.ts` (+ its spec — the pattern
|
|
to copy)
|
|
- The three offenders below
|
|
|
|
## Decisions (pre-made, don't relitigate)
|
|
|
|
- Unknown enum-ish values from the wire map to an explicit `Failure` (never a silent
|
|
fallback); specs pin that behavior.
|
|
- The MDX curriculum page ships in this WP (the WP that establishes an invariant teaches
|
|
it).
|
|
|
|
## Files
|
|
|
|
- `src/app/herregistratie/infrastructure/intake-policy.adapter.ts` — returns the raw DTO
|
|
resource with no parse; add `parseIntakePolicy(raw: unknown): Result<string, …>` and a
|
|
domain-side type
|
|
- `src/app/registratie/infrastructure/big-register.adapter.ts` (~line 25) —
|
|
`n.type as AantekeningType` → validated parse
|
|
- `src/app/brief/infrastructure/brief.adapter.ts` (~line 189) — `dto.scope as
|
|
PassageScope` → validated parse (the file is otherwise parse-heavy; this one field skips)
|
|
- New co-located specs: `intake-policy.adapter.spec.ts`, extend
|
|
`big-register.adapter.spec.ts` / `brief.adapter.spec.ts` (create if missing)
|
|
- New `src/docs/parse-dont-validate.mdx` — title `Foundations/Parse, don't validate`
|
|
|
|
## Steps
|
|
|
|
1. For each hole: write the small parser (pattern: `parseAanvraagStatus` in
|
|
`applications.adapter.ts`), wire it in, delete the cast.
|
|
2. Spec each parser: valid shape, missing field, unknown tag/enum value → `err`.
|
|
3. MDX page: the principle, value objects (`registratie/domain/value-objects/`), boundary
|
|
parsers, linking the three real call sites just fixed (before/after snippet).
|
|
|
|
## Acceptance criteria
|
|
|
|
- [x] No unvalidated `as <DomainType>` casts in `**/infrastructure/**` (the sanctioned
|
|
"narrow unknown to `Partial<Dto>` then parse" entry-cast is fine).
|
|
- [x] Each new parser has a spec including a rejection case.
|
|
- [x] MDX renders under Foundations in Storybook.
|
|
|
|
## Verification
|
|
|
|
GREEN + `npm run test-storybook:ci`. Smoke: intake wizard still loads its policy
|
|
(`npm start` → /intake).
|
|
|
|
## Out of scope
|
|
|
|
Runtime validation on _every_ endpoint (explicitly out of scope for the POC per
|
|
CLAUDE.md); `digid.adapter.ts` (faked auth, sanctioned).
|
|
|
|
## Risks
|
|
|
|
Seeded backend data containing values the new parsers reject → surfaces as `Failure` in
|
|
the UI; check the backend seed while writing the parsers and align the accepted set.
|