diff --git a/docs/project/backlog/README.md b/docs/project/backlog/README.md index d6c0750..bc5cf2c 100644 --- a/docs/project/backlog/README.md +++ b/docs/project/backlog/README.md @@ -43,44 +43,54 @@ WP-19's own file), so it's a separate manual/CI step, not chained into the other Gates land before the work they cover; each lint rule lands in the same WP as the fixes for its existing violations, so every WP ends green. -| WP | Title | Phase | Status | -| ------------------------------------------ | --------------------------------------------------------------------------------- | --------------------------- | ------ | -| [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done | -| [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done | -| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done | -| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | done | -| [WP-05](WP-05-parse-boundaries.md) | Parse-don't-validate closure + MDX | 1 · FP/DDD | done | -| [WP-06](WP-06-typed-async.md) | Generic async template contexts — kill `$any()` | 1 · FP/DDD | done | -| [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | done | -| [WP-08](WP-08-store-idiom.md) | One store idiom + machine naming + TEA MDX | 1 · FP/DDD | done | -| [WP-09](WP-09-pure-logic.md) | Pure-logic closure: dates + missing command specs | 1 · FP/DDD | done | -| [WP-10](WP-10-button-fidelity.md) | CIBG button fidelity | 2 · CIBG | done | -| [WP-11](WP-11-markup-fidelity.md) | CIBG markup fidelity: application-link + absent-class triage | 2 · CIBG | done | -| [WP-12](WP-12-datablock.md) | CIBG Datablock for application data | 2 · CIBG | done | -| [WP-13](WP-13-cibg-gap-register.md) | CIBG-gap register + hygiene + MDX | 2 · CIBG | done | -| [WP-14](WP-14-storybook-taxonomy.md) | Storybook taxonomy reorg + Layers MDX | 3 · Storybook | done | -| [WP-15](WP-15-missing-stories.md) | Missing stories: shell + brief components | 3 · Storybook | done | -| [WP-16](WP-16-component-a11y.md) | Component a11y: description wiring + alert role | 4 · a11y | done | -| [WP-17](WP-17-app-a11y.md) | App-level a11y: route focus, template lint, WCAG checklist | 4 · a11y | done | -| [WP-18](WP-18-abac-capability-spine.md) | ABAC capability spine (Principal + capabilities, phase P1) | 5 · productie-volwassenheid | done | -| [WP-19](WP-19-e2e-smoke.md) | Playwright e2e smoke | 5 · productie-volwassenheid | done | -| [WP-20](WP-20-second-locale.md) | Second locale proof | 5 · productie-volwassenheid | done | -| [WP-21](WP-21-resilience-seams.md) | Resilience seams (correlation-id, idempotency, retry) | 5 · productie-volwassenheid | done | -| [WP-22](WP-22-durable-persistence.md) | Durable persistence (optional tier) | 5 · productie-volwassenheid | done | -| [WP-23](WP-23-org-template-backend.md) | Org-template backend + admin role | 6 · Brief v2 | done | -| [WP-24](WP-24-letter-canvas.md) | Letter canvas (edit on the letter) | 6 · Brief v2 | done | -| [WP-25](WP-25-letter-preview-html.md) | Server-rendered letter preview (HTML; PDF deferred) | 6 · Brief v2 | done | -| [WP-26](WP-26-org-template-editor.md) | Admin org-template editor | 6 · Brief v2 | done | -| [WP-27](WP-27-brief-ux-layer.md) | Brief UX layer (undo/redo, standaardbrief, diff) | 6 · Brief v2 | done | -| [WP-28](WP-28-brief-v2-demo-polish.md) | Brief v2 demo polish (scenarios, e2e, docs) | 6 · Brief v2 | todo | -| [WP-29](WP-29-stamdata-beheer-editor.md) | Stamdata beheer editor (low-code, PR-emitting) | follow-on · ADR-0004 | done | -| [WP-30](WP-30-ci-perf-followups.md) | CI performance follow-ups (node_modules cache, runner image, path filters) | follow-on · CI/infra | todo | -| [WP-31](WP-31-shared-store-helpers.md) | Shared store helpers (ActionState/SaveState, history, debounced-save, RemoteData) | 7 · refinements | done | -| [WP-32](WP-32-stamdata-undo.md) | Undo/redo in the stamdata editor | 7 · refinements | done | -| [WP-33](WP-33-dev-switchers.md) | In-app dev switchers (scenario + role) | 7 · refinements | done | -| [WP-34](WP-34-adres-phone-brp-readonly.md) | Adres: phone field + BRP address read-only | 7 · refinements | done | -| [WP-35](WP-35-one-concept-per-type.md) | One Concept per case type (server-enforced) | 7 · refinements | done | -| [WP-36](WP-36-admin-cases.md) | Admin cases page + admin delete | 7 · refinements | done | +| WP | Title | Phase | Status | +| ---------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------- | ------ | +| [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done | +| [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done | +| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done | +| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | done | +| [WP-05](WP-05-parse-boundaries.md) | Parse-don't-validate closure + MDX | 1 · FP/DDD | done | +| [WP-06](WP-06-typed-async.md) | Generic async template contexts — kill `$any()` | 1 · FP/DDD | done | +| [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | done | +| [WP-08](WP-08-store-idiom.md) | One store idiom + machine naming + TEA MDX | 1 · FP/DDD | done | +| [WP-09](WP-09-pure-logic.md) | Pure-logic closure: dates + missing command specs | 1 · FP/DDD | done | +| [WP-10](WP-10-button-fidelity.md) | CIBG button fidelity | 2 · CIBG | done | +| [WP-11](WP-11-markup-fidelity.md) | CIBG markup fidelity: application-link + absent-class triage | 2 · CIBG | done | +| [WP-12](WP-12-datablock.md) | CIBG Datablock for application data | 2 · CIBG | done | +| [WP-13](WP-13-cibg-gap-register.md) | CIBG-gap register + hygiene + MDX | 2 · CIBG | done | +| [WP-14](WP-14-storybook-taxonomy.md) | Storybook taxonomy reorg + Layers MDX | 3 · Storybook | done | +| [WP-15](WP-15-missing-stories.md) | Missing stories: shell + brief components | 3 · Storybook | done | +| [WP-16](WP-16-component-a11y.md) | Component a11y: description wiring + alert role | 4 · a11y | done | +| [WP-17](WP-17-app-a11y.md) | App-level a11y: route focus, template lint, WCAG checklist | 4 · a11y | done | +| [WP-18](WP-18-abac-capability-spine.md) | ABAC capability spine (Principal + capabilities, phase P1) | 5 · productie-volwassenheid | done | +| [WP-19](WP-19-e2e-smoke.md) | Playwright e2e smoke | 5 · productie-volwassenheid | done | +| [WP-20](WP-20-second-locale.md) | Second locale proof | 5 · productie-volwassenheid | done | +| [WP-21](WP-21-resilience-seams.md) | Resilience seams (correlation-id, idempotency, retry) | 5 · productie-volwassenheid | done | +| [WP-22](WP-22-durable-persistence.md) | Durable persistence (optional tier) | 5 · productie-volwassenheid | done | +| [WP-23](WP-23-org-template-backend.md) | Org-template backend + admin role | 6 · Brief v2 | done | +| [WP-24](WP-24-letter-canvas.md) | Letter canvas (edit on the letter) | 6 · Brief v2 | done | +| [WP-25](WP-25-letter-preview-html.md) | Server-rendered letter preview (HTML; PDF deferred) | 6 · Brief v2 | done | +| [WP-26](WP-26-org-template-editor.md) | Admin org-template editor | 6 · Brief v2 | done | +| [WP-27](WP-27-brief-ux-layer.md) | Brief UX layer (undo/redo, standaardbrief, diff) | 6 · Brief v2 | done | +| [WP-28](WP-28-brief-v2-demo-polish.md) | Brief v2 demo polish (scenarios, e2e, docs) | 6 · Brief v2 | todo | +| [WP-29](WP-29-stamdata-beheer-editor.md) | Stamdata beheer editor (low-code, PR-emitting) | follow-on · ADR-0004 | done | +| [WP-30](WP-30-ci-perf-followups.md) | CI performance follow-ups (node_modules cache, runner image, path filters) | follow-on · CI/infra | todo | +| [WP-31](WP-31-shared-store-helpers.md) | Shared store helpers (ActionState/SaveState, history, debounced-save, RemoteData) | 7 · refinements | done | +| [WP-32](WP-32-stamdata-undo.md) | Undo/redo in the stamdata editor | 7 · refinements | done | +| [WP-33](WP-33-dev-switchers.md) | In-app dev switchers (scenario + role) | 7 · refinements | done | +| [WP-34](WP-34-adres-phone-brp-readonly.md) | Adres: phone field + BRP address read-only | 7 · refinements | done | +| [WP-35](WP-35-one-concept-per-type.md) | One Concept per case type (server-enforced) | 7 · refinements | done | +| [WP-36](WP-36-admin-cases.md) | Admin cases page + admin delete | 7 · refinements | done | +| [WP-37](WP-37-dev-switcher-reset.md) | Dev-switcher reset fix (scenario/role URL param) | 8 · platform/DX/showcase | done | +| [WP-38](WP-38-dependency-graph-boundaries.md) | Dependency graph + declarative boundaries (visualize + enforce) | 8 · platform/DX/showcase | todo | +| [WP-39](WP-39-showcase-snippets-animations.md) | Showcase: linked code snippets + teaching animations | 8 · platform/DX/showcase | todo | +| [WP-40](WP-40-pii-kernel.md) | PII kernel: branded `Bsn` VO (elfproef) + masked-value atom | 8 · platform/DX/showcase | todo | +| [WP-41](WP-41-persisted-authz-audit.md) | Persisted, queryable authz/PII-reveal audit (no PII) | 8 · platform/DX/showcase | todo | +| [WP-42](WP-42-privacy-security-showcase.md) | Privacy & security showcase page (mask + no-PII log) | 8 · platform/DX/showcase | todo | +| [WP-43](WP-43-scaffold-generators.md) | Runnable generators: value-object / form-machine / bff-endpoint / ui-component | 8 · platform/DX/showcase | todo | +| [WP-44](WP-44-context-generator.md) | Runnable generator: `gen:context` | 8 · platform/DX/showcase | todo | +| [WP-45](WP-45-create-ssp-generator.md) | `create-ssp` bootstrap generator (mechanise new-ssp) | 8 · platform/DX/showcase | todo | +| [WP-46](WP-46-vitest-coverage.md) | Vitest coverage (report + report-only thresholds) | 8 · platform/DX/showcase | todo | Sequencing dependencies (stated in the WPs too): 01 before 10–15 (axe covers story churn); 03/04 before 05–09 (boundaries stop new violations during refactors); 06 before 07 (typed @@ -95,6 +105,13 @@ Phase 6 (Brief v2, the "Brief opstellen v2" PRD) is strictly ordered `letter.css` contract, 26 needs 23's endpoints + 24's canvas, 27/28 polish on top. Phase 7 (refinements, WP-31..36): only 31 → 32 is ordered (32 reuses the `createHistory` helper 31 extracts); 33–36 are independent — pick any order. +Phase 8 (platform/DX/showcase, WP-37..46): goal is BOTH a teaching showcase AND a production +starter template. Priority: WP-37 (P0 quick fix) → WP-46 + WP-38 + WP-39 (P1, parallel) → +WP-40 (P2) → WP-43 (P3) → WP-41 → WP-42 → WP-44 → WP-45 (P4). Ordering constraints: +41 needs 40's PII kernel; 42 needs 40+41; 44 (`gen:context`) needs 38 (declarative boundaries +make the generator simple) + 43; 45 (`create-ssp`) needs 43+44. 37/38/39/40/43/46 are otherwise +independent. Two open tool forks, decided as step 1 of their WP: 38 dependency-cruiser vs Sheriff; +43 plop vs Angular schematics. ## WP template diff --git a/docs/project/backlog/WP-38-dependency-graph-boundaries.md b/docs/project/backlog/WP-38-dependency-graph-boundaries.md new file mode 100644 index 0000000..eab2487 --- /dev/null +++ b/docs/project/backlog/WP-38-dependency-graph-boundaries.md @@ -0,0 +1,38 @@ +# WP-38 — Dependency graph + declarative boundaries + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P1 + +## Why + +Bounded-context + atomic-layer boundaries are enforced only by hand-duplicated +`no-restricted-imports` blocks in `eslint.config.mjs` — pass/fail, no graph, and brittle: the +`new-context` skill literally says "grep the config and copy a block", and `herregistratie` is +missing its explicit ban block (asymmetry). We want to **see** the dependencies AND **enforce** +them from one declarative source. + +## Decisions + +- **Step 1 — tool fork:** dependency-cruiser (recommended: graph + CI rules on plain Angular) vs + Sheriff (tag-based, DDD/atomic-native, weaker graph). Decide before building. +- Encode context + layer rules once (contexts `shared/auth/registratie/herregistratie/brief/beheer/ +showcase`; layers `domain/application/infrastructure/contracts/ui`); **fix the herregistratie gap**. +- Keep ESLint for the intra-file rules it does better (`domain↛@angular`, ApiClient value-import + confinement, `no-explicit-any`); migrate only the cross-module _direction_ rules to the new tool. +- Emit a graph the showcase/teaching can reuse (feeds WP-39). + +## Files + +- New: `.dependency-cruiser.js` (or `sheriff.config.ts`); `npm run graph` + validate script. +- `scripts/ci-local.sh` + `.github/workflows/ci.yml` — add `depcruise --validate` (non-optional). +- `eslint.config.mjs` — remove the migrated direction rules (keep the rest). +- New doc `docs/reference/architecture/dependencies.md`; embed the graph in a Foundations page. +- `.claude/skills/new-context/SKILL.md` — point at the single declarative source. + +## Acceptance criteria + +- [ ] One declarative config expresses all allowed context/layer edges; herregistratie included. +- [ ] `npm run graph` produces an architecture graph (SVG/HTML); validate runs in `npm run ci`. +- [ ] A deliberately-illegal import fails the validate step (proven, then reverted). +- [ ] No loss of enforcement vs the old ESLint blocks; `npm run ci` green. diff --git a/docs/project/backlog/WP-39-showcase-snippets-animations.md b/docs/project/backlog/WP-39-showcase-snippets-animations.md new file mode 100644 index 0000000..e0f2a86 --- /dev/null +++ b/docs/project/backlog/WP-39-showcase-snippets-animations.md @@ -0,0 +1,35 @@ +# WP-39 — Showcase: linked code snippets + teaching animations + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P1 + +## Why + +The `/concepts` showcase shows live components, but its code snippets are hand-authored +`innerHTML` strings with manual highlighting — they drift from the real code. And the animation +toolkit (`withViewTransitions`, native `animate.enter/leave`, reduced-motion-safe keyframes in +`styles.scss`) is proven in dashboard/upload but unused for teaching motion. + +## Decisions + +- **Anti-drift snippets:** load _real_ source excerpts via Vite `?raw` imports with `// #region name` + markers + a tiny pure extractor; render with lightweight highlighting. No more `innerHTML` literals. +- On the Storybook/MDX side, use ``/Canvas "show code" consistently (extend beyond the + current 4 of 14 pages). +- **Reuse** the existing motion toolkit (no new animation lib): `animate.enter/leave` on the + fold/union demos + a "step through `reduce`" animation for the TEA loop; keep reduced-motion-safe. + +## Files + +- `src/app/showcase/concepts.page.ts` — replace `innerHTML` snippets with `?raw`+region excerpts; + apply animations. +- New tiny extractor util (pure) + spec (region slicing). +- `src/docs/machines.mdx`, `fp-in-ui.mdx`, `remote-data.mdx` — reference the live linked snippets. + +## Acceptance criteria + +- [ ] Every showcase snippet is sourced from a real file (edit the source → snippet updates); none + hand-copied. +- [ ] The TEA/fold demos animate (reduced-motion respected); no new animation dependency. +- [ ] Storybook a11y stays green; `npm run ci` green. diff --git a/docs/project/backlog/WP-40-pii-kernel.md b/docs/project/backlog/WP-40-pii-kernel.md new file mode 100644 index 0000000..a5c5277 --- /dev/null +++ b/docs/project/backlog/WP-40-pii-kernel.md @@ -0,0 +1,34 @@ +# WP-40 — PII kernel: branded `Bsn` VO + masked-value atom + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P2 + +## 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. +- `` 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 + +- [ ] `parseBsn` accepts valid elfproef numbers, rejects bad checksum/length (spec). +- [ ] `` renders masked by default with an accessible reveal; behandel-scherm uses it. +- [ ] Pure maskers have specs (no TestBed); backend `MaskTail` parity noted; `npm run ci` green. diff --git a/docs/project/backlog/WP-41-persisted-authz-audit.md b/docs/project/backlog/WP-41-persisted-authz-audit.md new file mode 100644 index 0000000..9cafbb3 --- /dev/null +++ b/docs/project/backlog/WP-41-persisted-authz-audit.md @@ -0,0 +1,34 @@ +# WP-41 — Persisted, queryable authz/PII-reveal audit + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P2 +Depends on: WP-40 + +## Why + +The security-relevant events (authz denials via `AuditAuthz`, BIG-nummer reveal, step-up) are +**log-only** — never persisted, so there's no queryable trail. The one persisted `AuditEntry` table +covers document lifecycle only. PRD-0002 §8 calls for a persisted authorization audit. A register +(BIO) wants this queryable, and strictly data-minimised. + +## Decisions + +- Persist authz/reveal/step-up events into a data-minimised EF table extending the existing + `AuditEntry`/`AppDbContext` pattern: role, action, resource-ref, decision, correlation-id, + timestamp — **never** the value or any PII. +- Wire `AuditAuthz` + the reveal endpoint to persist (in addition to the log line). +- Optional thin admin read view — reuse `cases:manage` or add an `audit:read` capability. + +## Files + +- `backend/.../Data/` — new audit entity + `AppDbContext` DbSet + EF migration. +- `backend/.../Program.cs` — `AuditAuthz` + reveal endpoint persist. +- Backend tests: events are persisted; **no PII columns**; correlation-id captured. +- (Optional) FE audit read page + adapter/store, gated by the capability. + +## Acceptance criteria + +- [ ] Denials, reveals, and step-up attempts land as rows with no PII/value fields. +- [ ] A test asserts the schema carries no name/bsn/value column. +- [ ] `dotnet test` + `npm run ci` green; api-client drift clean if endpoints added. diff --git a/docs/project/backlog/WP-42-privacy-security-showcase.md b/docs/project/backlog/WP-42-privacy-security-showcase.md new file mode 100644 index 0000000..5a26b06 --- /dev/null +++ b/docs/project/backlog/WP-42-privacy-security-showcase.md @@ -0,0 +1,31 @@ +# WP-42 — Privacy & security showcase page + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P2 +Depends on: WP-40, WP-41 + +## Why + +Once the reusable privacy pieces exist (WP-40 masked-value atom + pure maskers, WP-41 persisted +no-PII audit), showcase them as a teaching artifact: how to **mask** and **log** PII safely with FP +(pure functional core) + atomic design (the masked-value atom), tied to the existing reveal + +step-up + capability slice. + +## Decisions + +- A Foundations MDX + a small `showcase` demo (showcase context may read every context). +- Teach the principles explicitly: data-minimisation, PII out of logs, masked-by-default + + audited reveal, server as authority — framed for a Dutch register (AVG art. 9, BIO). +- Reuse WP-39's linked-snippet mechanism so the shown code can't drift. + +## Files + +- New `src/docs/privacy-security.mdx` (Foundations). +- New/extended `showcase` demo wiring ``, the pure maskers, and the reveal flow. + +## Acceptance criteria + +- [ ] Page demonstrates mask + no-PII logging with live components and linked (non-drifting) code. +- [ ] Explains the register rationale (AVG/BIO/data-minimisation) concisely. +- [ ] Storybook a11y green; `npm run ci` green. diff --git a/docs/project/backlog/WP-43-scaffold-generators.md b/docs/project/backlog/WP-43-scaffold-generators.md new file mode 100644 index 0000000..8b337c6 --- /dev/null +++ b/docs/project/backlog/WP-43-scaffold-generators.md @@ -0,0 +1,31 @@ +# WP-43 — Runnable leaf generators + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P3 + +## Why + +The house patterns (value object, form machine, bff endpoint, ui component) are documented only as +agent-executed prose skills; there's no `npm run gen:*` a developer runs to emit the boilerplate. +For a template meant to bootstrap real registers, mechanise the highest-churn recipes. + +## Decisions + +- **Step 1 — tool fork:** plop (recommended: tiny, readable templates, `npm run gen:*`) vs Angular + schematics (native `ng generate`, heavier to author). Decide before building. +- Generators emit house-pattern files **with co-located spec/story**, matching current idioms: + `gen:value-object`, `gen:form-machine` (page/wizard), `gen:bff-endpoint`, `gen:ui-component`. +- Templates become the single source; each matching skill gets a one-line "run `npm run gen:x`, then …". + +## Files + +- New `plopfile.mjs` (or `schematics/`) + template files; `package.json` `gen:*` scripts. +- Update the corresponding `.claude/skills/*/SKILL.md` to invoke the generator. + +## Acceptance criteria + +- [ ] Each generator emits files that pass `npm run lint` + `npm run ci` with no hand-edits beyond + filling in the domain specifics. +- [ ] Generated units include their spec/story per house rules. +- [ ] A smoke run of each generator is documented; `npm run ci` green. diff --git a/docs/project/backlog/WP-44-context-generator.md b/docs/project/backlog/WP-44-context-generator.md new file mode 100644 index 0000000..e0c882a --- /dev/null +++ b/docs/project/backlog/WP-44-context-generator.md @@ -0,0 +1,32 @@ +# WP-44 — `gen:context` generator + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P3 +Depends on: WP-38, WP-43 + +## Why + +Adding a bounded context is currently a manual multi-file edit (folders + tsconfig alias + copied +ESLint boundary block + lazy route) — the `new-context` skill's most error-prone steps. Mechanise it. + +## Decisions + +- `gen:context` (same tool as WP-43) emits `{domain,application,infrastructure,ui,contracts}` + folders + the `@ctx/*` tsconfig path alias + a lazy route + the boundary entry. +- **Simpler because of WP-38:** boundaries are declarative by then, so the generator adds one config + entry instead of hand-copying ESLint blocks. +- Replaces the manual portion of the `new-context` skill. + +## Files + +- Extend the WP-43 generator setup with a `context` generator + templates. +- Wire tsconfig alias + boundary config + route insertion (idempotent edits). +- Update `.claude/skills/new-context/SKILL.md`. + +## Acceptance criteria + +- [ ] `npm run gen:context ` produces a context that lints clean (boundaries recognised) and + routes lazily. +- [ ] Boundary tool (WP-38) validates the new context's allowed edges. +- [ ] `npm run ci` green. diff --git a/docs/project/backlog/WP-45-create-ssp-generator.md b/docs/project/backlog/WP-45-create-ssp-generator.md new file mode 100644 index 0000000..ee556a0 --- /dev/null +++ b/docs/project/backlog/WP-45-create-ssp-generator.md @@ -0,0 +1,32 @@ +# WP-45 — `create-ssp` bootstrap generator + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P4 +Depends on: WP-43, WP-44 + +## Why + +`new-ssp` is a manual fork-and-strip recipe. To actually bootstrap a production-ready frontend for +a different Dutch register quickly, mechanise it into a runnable script that produces a clean +scaffold keeping the enforced architecture + shared building blocks. + +## Decisions + +- A `create-ssp` script (Node/plop-driven) that clones-and-strips per the `new-ssp` recipe: + drop the business contexts, prune their routes/aliases/boundary entries, rename `BigRegister.*`, + re-point the `--rhc-*` token bridge, re-seed the backend, re-run `gen:api`. +- Keep-as-is list from `new-ssp` (shared kernel/ui/layout, eslint/boundary config, check-tokens, + CI, nswag, storybook, auth shell, ADRs, CLAUDE.md, the skills). +- Reuse WP-44's `gen:context` to seed the first real context of the new portal. + +## Files + +- New `scripts/create-ssp.*` (+ any templates); documented in `.claude/skills/new-ssp/SKILL.md`. + +## Acceptance criteria + +- [ ] Running it yields a portal that builds, lints (boundaries intact), and passes `npm run ci` + with no business contexts left over. +- [ ] The generated scaffold's `gen:api` + token bridge point at the new backend/theme. +- [ ] Documented end-to-end in the `new-ssp` skill. diff --git a/docs/project/backlog/WP-46-vitest-coverage.md b/docs/project/backlog/WP-46-vitest-coverage.md new file mode 100644 index 0000000..2c33025 --- /dev/null +++ b/docs/project/backlog/WP-46-vitest-coverage.md @@ -0,0 +1,34 @@ +# WP-46 — Vitest coverage (report + thresholds) + +Status: todo +Phase: 8 — platform/DX/showcase +Priority: P1 + +## Why + +Tests run via the `@angular/build:unit-test` (Vitest) builder but report no coverage — there's no +visibility into what the house test rules actually cover. A template/showcase should surface this. + +## Decisions + +- **Step 1 — verify wiring:** confirm how coverage is enabled with the Angular unit-test builder + (`codeCoverage` option and/or a vitest config), add `@vitest/coverage-v8`. +- `npm run test:coverage` → text + HTML/lcov report. +- **Report-only first** (ponytail: visibility before a blocking gate, like the semgrep job). + Threshold focus on the layers the house rules already demand tests for (`domain/**`, pure + combinators, `parse*` boundaries, reducers), not a blanket global %; exclude stories, the + generated `api-client.ts`, and DTOs/contracts. +- Wire into `ci-local.sh`/CI **non-blocking**, with a documented path to flip on enforcement later. +- **Open choice:** report-only vs enforce a minimum now. + +## Files + +- `package.json` (`test:coverage` + dep), coverage config (builder option or vitest config). +- `scripts/ci-local.sh` / `.github/workflows/ci.yml` — non-blocking coverage step. +- `.gitignore` — ignore the coverage output dir. + +## Acceptance criteria + +- [ ] `npm run test:coverage` produces a report (text + HTML/lcov) with sensible excludes. +- [ ] Coverage runs in CI without failing the build (report-only). +- [ ] `npm run ci` green.