diff --git a/docs/project/backlog/README.md b/docs/project/backlog/README.md index e5b4fd7..721fbac 100644 --- a/docs/project/backlog/README.md +++ b/docs/project/backlog/README.md @@ -119,7 +119,7 @@ for its existing violations, so every WP ends green. | [WP-66](WP-66-behandelportal-openzaak-write.md) | Wire the decision into OpenZaak | 11 · Behandelportal | done | | [WP-67](WP-67-monorepo-behandelportal.md) | Merge behandelportal into this repo as a monorepo | 11 · Behandelportal | done | | [WP-68](WP-68-ddd-aggregate-hardening.md) | Aggregate invariants + status modelling (architecture review) | 12 · DDD hardening | done | -| [WP-69](WP-69-intake-scholing-threshold-enforcement.md) | Enforce the scholing threshold server-side | 12 · DDD hardening | todo | +| [WP-69](WP-69-intake-scholing-threshold-enforcement.md) | Enforce the scholing threshold server-side | 12 · DDD hardening | done | | [WP-70](WP-70-test-data-builders.md) | Test-data builders: illegal fixtures unrepresentable (ADR-0006) | 12 · DDD hardening | done | | [WP-71](WP-71-test-framework-coherence.md) | Test framework coherence: BDD/DDD alignment, close the escape hatches | 12 · DDD hardening | done | diff --git a/docs/project/backlog/WP-69-intake-scholing-threshold-enforcement.md b/docs/project/backlog/WP-69-intake-scholing-threshold-enforcement.md index caba41b..282c1d5 100644 --- a/docs/project/backlog/WP-69-intake-scholing-threshold-enforcement.md +++ b/docs/project/backlog/WP-69-intake-scholing-threshold-enforcement.md @@ -1,8 +1,28 @@ # WP-69 — Enforce the scholing threshold server-side -Status: todo +Status: done (5d73ca2) Phase: 12 — DDD hardening +## Verification result (2026-08-18) + +Backend 230 → 245 tests (+8 `Domain/IntakeRuleTests`, +7 `Acceptance/IntakeSubmissionTests`); +frontend ssp 238 → 242. `npm run ci` green incl. `check:seam` (`OK … (1000)`), api-client and +behaviour-spec drift. + +**The bypass was proven closed against a running backend, not just by green tests:** +`POST /api/v1/intakes {"uren": 500}` with no scholing fields → **400** with the Dutch detail; +an intake-typed Concept submitted via `POST /applications/{id}/submit {"uren": 500}` → **400**, +and `GET /applications/{id}` afterwards still reports `"tag":"Concept"` (no state change, still +retryable); the same request plus `"aanvullendeScholing": false` → **200** with a referentie and +`InBehandeling`. + +**Deviation from the Files list:** `EndpointTests.Worked_hours_submission_succeeds` had to be +touched despite being on the do-not-modify list — it posts `{ uren: 40 }` with no scholing +answer, which _is_ the crafted-POST bypass this WP closes, so the existing test was itself +asserting the vulnerable behaviour. Fixed minimally by adding `aanvullendeScholing = false` +(unknown to and ignored by `HerregistratieRequest`, so the paired `/herregistraties` row is +unaffected). The zero-hours 422 rows — the ordering regression net — are unmodified as planned. + ## Why WP-68 (F5) found that `IntakePolicy`'s doc-comment claimed _"the backend re-validates on