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:
@@ -0,0 +1,52 @@
|
||||
# WP-40 — PII kernel: branded `Bsn` VO + masked-value atom
|
||||
|
||||
Status: done
|
||||
Phase: 8 — platform/DX/showcase
|
||||
Priority: P2
|
||||
|
||||
## Outcome
|
||||
|
||||
- `shared/kernel/bsn.ts` — `Bsn` branded VO + `parseBsn` with the **elfproef** (11-test) checksum
|
||||
(+spec). Wired into the DigiD login boundary (`digid.adapter`): login now does real BSN
|
||||
validation instead of "any 9 digits" — login hint + e2e BSNs updated to a valid one (`123456782`).
|
||||
- `shared/kernel/pii.ts` — pure `maskBsn`/`maskTail`/`REDACTED` (+spec), the functional core. Moved
|
||||
out of `debug-state/mask.ts`, which keeps only `redactProfile` (it depends on the registratie
|
||||
`BigProfile`, so it can't live in `shared/kernel` — boundary; debug-state is the sanctioned
|
||||
cross-context devtool). `mask.spec` ported to `pii.spec`.
|
||||
- `<app-masked-value>` atom (`shared/ui/masked-value`) + story — masked-by-default value with an
|
||||
optional reveal affordance; **centralises** the `.includes('*')` masked-detection that
|
||||
`behandel-scherm` used to sniff inline (now refactored to the atom). The atom only emits
|
||||
`reveal`; the caller keeps the step-up confirm + audited fetch.
|
||||
- Deviation (flagged): `Session.bsn` stays typed `string` — the brand guards the login ENTRY, but
|
||||
Session crosses a persistence boundary where the restore path deliberately drops the bsn
|
||||
(privacy) and JSON can't carry a brand, so re-typing it would add a `''`/nullable ripple for no
|
||||
real gain. Backend `MaskTail` twin unchanged (still in sync).
|
||||
|
||||
## Why
|
||||
|
||||
Masking is real but ad-hoc: pure helpers live in `debug-state/mask.ts`, the BSN is a bare `string`
|
||||
in `session.ts` (no branded VO / checksum, unlike `BigNummer`/`Postcode`), and masked state is
|
||||
detected by `.includes('*')` sniffing — no reusable atomic-design component. For a register this is
|
||||
AVG art. 9 (BSN = special category) territory; the pieces should be first-class and reusable.
|
||||
|
||||
## Decisions
|
||||
|
||||
- `Bsn` branded value object + `parseBsn` with the **elfproef** (9 digits, weighted mod-11)
|
||||
checksum — parity with the other value objects. Replace the bare `string` in `session.ts`.
|
||||
- Consolidate the pure maskers into `shared/kernel/pii.ts` (functional core): `maskBsn`, `maskTail`,
|
||||
`redactProfile`, … Keep the backend `MaskTail` twin in sync.
|
||||
- `<app-masked-value>` atom (`shared/ui`) — masked by default, optional reveal affordance + a11y;
|
||||
replaces `.includes('*')` sniffing and composes into the behandel-scherm reveal.
|
||||
|
||||
## Files
|
||||
|
||||
- New `src/app/shared/kernel/bsn.ts` (+spec, elfproef cases) and `shared/kernel/pii.ts` (+spec).
|
||||
- `src/app/auth/domain/session.ts` — `bsn: Bsn`.
|
||||
- New `src/app/shared/ui/masked-value/` atom (+ story).
|
||||
- `src/app/shared/ui/debug-state/mask.ts` — re-export/move to the kernel module.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] `parseBsn` accepts valid elfproef numbers, rejects bad checksum/length/all-zeros (spec).
|
||||
- [x] `<app-masked-value>` renders masked by default with an optional reveal; behandel-scherm uses it (no more inline sniff).
|
||||
- [x] Pure maskers have specs (no TestBed); backend `MaskTail` parity noted; `npm run ci` green.
|
||||
Reference in New Issue
Block a user