# WP-09 — Pure-logic closure: dates + missing command specs Status: done Phase: 1 — FP/DDD core ## Why Three hand-rolled copies of the same `nl-NL {day: numeric, month: long, year: numeric}` formatter exist with divergent signatures, while other components use the Angular `date` pipe — two mechanisms plus drift risk. And two application commands with real logic have no spec despite "domain and pure logic must have a spec" (CLAUDE.md §5). ## Read first - `src/app/registratie/domain/tasks.ts` (~line 15, `formatNL(d: Date)`) - `src/app/registratie/ui/aanvraag-block/aanvraag-block.component.ts` (~line 96, `formatNL(iso?: string)`) - `src/app/brief/ui/letter-preview/letter-preview.component.ts` (~line 96, inline copy) - `src/app/registratie/application/draft-sync.ts` (debounce logic) - `src/app/registratie/application/submit-change-request.ts` - `src/app/app.config.ts` (LOCALE_ID `nl` is already set) ## Decisions (pre-made, don't relitigate) - **Templates use `DatePipe`; pure TS uses one `formatDatumNl`** in `src/app/shared/kernel/datum.ts` (Intl.DateTimeFormat, accepts ISO string | Date, empty-safe). Rule goes into CLAUDE.md conventions. - Kernel naming is Dutch (`datum.ts`) — it formats for the Dutch UI; if that clashes with the "shared = English" rule during implementation, `format-date-nl.ts` is the alternative — pick one and note it here. ## Files - New `src/app/shared/kernel/datum.ts` + `datum.spec.ts` - The three formatter copies (delete, redirect to pipe or kernel fn) - New `src/app/registratie/application/draft-sync.spec.ts` - New `src/app/registratie/application/submit-change-request.spec.ts` - `CLAUDE.md` conventions (one line: DatePipe in templates, `formatDatumNl` in pure TS) ## Steps 1. Write `formatDatumNl` + spec (pin exact expected strings, e.g. `2 juli 2026`; undefined/empty → `''`). 2. Replace the three copies; templates that can use the pipe use the pipe. 3. `draft-sync.spec.ts` with vitest fake timers: coalescing (n rapid changes → 1 sync), trailing call, flush-on-submit if applicable. 4. `submit-change-request.spec.ts`: ok path, error path (stubbed client), Result shape. 5. Also sweep the tiny dead exports flagged in the audit: remove unused `map3` from `shared/kernel/fp.ts` (verify unused first) and the never-set `variant` input on `confirmation.component.ts`. ## Acceptance criteria - [x] Exactly one hand-written date formatter in the repo. `formatDatumNl` uses `Intl.DateTimeFormat(...).format()` rather than `.toLocaleDateString()`, so `grep -rn "toLocaleDateString" src/app` now hits **nothing** (stronger than the literal criterion, same intent — no file anywhere hand-rolls date formatting). - [x] Both command specs exist; debounce coalescing + error path covered (`draft-sync.spec.ts`, `submit-change-request.spec.ts`). - [x] `map3` removed (found in `shared/application/remote-data.ts`, not `shared/kernel/fp.ts` as the WP text guessed — updated the three docs that mentioned it: CLAUDE.md, `docs/ARCHITECTURE.md`, `remote-data.mdx`). The `variant` input on `confirmation.component.ts` no longer exists — already cleaned up before this WP ran; nothing to do. - [x] CLAUDE.md rule added (`Conventions` — DatePipe in templates, `formatDatumNl` in pure TS). ## Verification GREEN + `npm run test-storybook:ci` (208 unit tests, up from WP-08's 201 by the 7 new specs; 137 Storybook/a11y unchanged). Manual smoke via a running `docker compose` stack + Playwright: dashboard's herregistratie-deadline task text ("Verleng uw registratie vóór 1 maart 2027"), the Concept aanvraag-block's complete-before text ("Rond de aanvraag af vóór 2 augustus 2026"), and `formatDatumNl` unit specs for the letter-preview's `today` — all render the expected long-form Dutch date, no console errors. ## Out of scope `Result` combinators (map/andThen/fold) — nice-to-have from the old roadmap, not in this backlog. ## Risks Node ICU vs browser locale output differences — the spec pins strings; if CI's Node lacks full ICU (it shouldn't on Node 24), pin via `Intl.DateTimeFormat('nl-NL', …)` explicitly.