docs(adr-0002): accept, and record the unbuilt Principal union as debt

ADR-C-005 from the ADR-conformance pass.

Status Proposed -> Accepted. Two apps have shipped against this ADR and its
structural rulings run in CI at severity: error with 0 violations; the other
five ADRs are all Accepted. A decision CI enforces is not "Proposed".

Drops two "out of scope, not built" bullets that have since shipped
(WP-61..67) — the Behandeling backoffice, and the backend status lifecycle +
authz DTOs: AanvraagStatusTag, GET /me (Program.cs:578), Domain/
Authorization/Authz.cs. Real authentication is the one that genuinely stays.

Replaces the `Session -> Principal` deferral with a Known debt section. The
deferral was conditional on the backoffice not existing yet; it does now, and
the union did not follow. `grep -rn "Principal" apps libs` returns one hit,
a comment. Consequently the two auth contexts are byte-identical (diff -rq:
zero content differences), and behandelportal's Behandelaar still carries a
bsn and logs in through DigiD — a backoffice user authenticating as a
citizen, which is what §3 was written to prevent. The divergence that did
happen took an orthogonal side door (medewerker.interceptor.ts) that never
touches Session.

The section says explicitly that the WP-67 amendment's "expected to diverge"
reasoning still holds but has never been tested, so the identical copies are
evidence §3 is unexecuted — not evidence §3 was wrong. Without that, a future
reader is likely to "simplify" the duplication away and cement the citizen
login into the backoffice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-08-26 17:48:48 +02:00
co-authored by Claude Opus 5
parent 664a43bf2d
commit 4debf6614f
@@ -1,6 +1,6 @@
# ADR 0002 — User groups as actors, not bounded contexts # ADR 0002 — User groups as actors, not bounded contexts
Status: Proposed · Date: 2026-07-01 Status: Accepted · Date: 2026-07-01 · Amended 2026-08-01 (WP-67)
## Problem ## Problem
@@ -160,11 +160,35 @@ changes is purely the _packaging_:
## Out of scope here (next steps, not built) ## Out of scope here (next steps, not built)
- Building the Behandeling backoffice application.
- Real authentication: DigiD (SSP) and employee SSO / eHerkenning (backoffice). - Real authentication: DigiD (SSP) and employee SSO / eHerkenning (backoffice).
- The `auth` `Session → Principal` refactor — deferred until a second actor is actually introduced.
- The backend aanvraag status lifecycle + authorization endpoints/DTOs.
ponytail: this ADR draws the boundaries so nothing has to be undone later; it does **not** scaffold a Two bullets that stood here — building the Behandeling backoffice, and the backend aanvraag
second app or a role system now. Introduce the `Principal` union and the status lifecycle when the status lifecycle + authorization endpoints/DTOs — **shipped** (WP-61…WP-67): `apps/behandelportal`,
backoffice work actually starts — YAGNI until then. `AanvraagStatusTag` (`Domain/Applications/AanvraagStatus.cs`), `GET /me` (`Program.cs:578`),
`Domain/Authorization/Authz.cs`.
## Known debt: `Session → Principal` was never built
§3's `Principal` union is the one decision here that has **not** been executed, and it is now
debt rather than a deferral. Actor #2 arrived — `apps/behandelportal` shipped — and the union
did not follow. `grep -rn "Principal" apps libs` returns a single hit: a comment in
`libs/shared/src/infrastructure/role.ts`. There is no such type.
What that omission actually costs, measured 2026-08-26:
- `apps/ssp/src/app/auth` and `apps/behandelportal/src/app/auth` are byte-identical —
`diff -rq` reports **zero** content differences across 9 of 11 files, the only delta being
two extra files in behandelportal.
- `behandelportal`'s Behandelaar still carries a `bsn` and logs in through `DigidAdapter`.
A backoffice user authenticates as a citizen, which is precisely what §3 was written to prevent.
- The divergence that _did_ occur took an orthogonal side door — `medewerker.interceptor.ts`,
a dev-only `X-Medewerker` header stamp that never touches `Session`.
The WP-67 amendment above justifies keeping `auth` duplicated on the grounds that it is
"expected to diverge". That reasoning still holds — but it has never been **tested**, because
the change that would test it is this one. Read the two identical copies as evidence that
§3 is unexecuted, not as evidence that §3 was wrong.
ponytail: this ADR draws the boundaries so nothing has to be undone later. The original
"YAGNI until the backoffice work starts" call was right when written and has now expired —
the backoffice started. `Principal` is owed.