# WP-69 — Enforce the scholing threshold server-side Status: todo Phase: 12 — DDD hardening ## Why WP-68 (F5) found that `IntakePolicy`'s doc-comment claimed _"the backend re-validates on submit as the authority"_ — it doesn't. `GET /intake/policy` only echoes `ScholingThreshold`; neither `SubmitApplicationRequest` (`DiplomaHerkomst`, `Uren`, `Documents`) nor `IntakeRequest` (`Uren`) carries a scholing answer at all, so there's nothing for the server to re-validate. Both submit paths apply only `SubmissionRules.RejectZeroUren`. A crafted POST — bypassing the wizard entirely — can skip the scholing requirement (`scholingGevolgd`/`punten` in `intake.machine.ts`) even though it's presented as mandatory in the UI. ADR-0001's canonical "config value" example (the FE applies the threshold for instant feedback, the backend re-validates as authority) is unenforced for the one rule it was written to illustrate. ## Read first - `backend/src/BigRegister.Api/Domain/Intake/IntakePolicy.cs` (the corrected doc-comment, WP-68) - [ADR-0001 — BFF-lite + decision DTOs](../../reference/architecture/0001-bff-lite-decision-dtos.md) §"config value" - `apps/ssp/src/app/herregistratie/domain/intake.machine.ts` (`lageUren`, `scholingGevolgd`, `punten` — the wizard's existing FE-side rule and its answers) - `backend/src/BigRegister.Api/Contracts/Dtos.cs` (`SubmitApplicationRequest`, `IntakeRequest`, `DocumentRefDto`) - `backend/src/BigRegister.Api/Program.cs` — the `intakes` and `applications/{id}/submit` endpoints ## Decisions Not yet made — this is a placeholder WP opened by WP-68, not a ready-to-implement one. Needs a `planner` pass before work starts. Open questions to resolve then: - The request DTOs need a scholing answer field (likely mirroring `intake.machine.ts`'s `ValidIntake.aanvullendeScholing`/`punten`) — this is a wire change, so it touches `contracts/`, the wizard's submit payload, and `npm run gen:api`. - Whether to add the rule to `SubmissionRules` (alongside `RejectZeroUren`) or give `IntakePolicy` its own `RejectMissingScholing(uren, scholing)`, matching the class that already owns the threshold. - Reading the answer out of the wizard's `Draft` JSON was rejected in WP-68 — the backend's documented posture is that the draft is opaque (`AppDbContext`'s header comment) — so the answer must arrive as an explicit request field, not be extracted from the opaque snapshot. ## Out of scope (for now) Implementation — this WP exists to track the gap; do not implement without a Decisions block.