Files
atomic-design-poc/docs/project/refactor-backlog-setup/refactor-backlog/00-baseline.md
T
ehoandClaude Opus 5 664a43bf2d docs: refactoring-backlog workspace — baseline + 3 Phase 1 agents
Runs the multi-agent refactoring-backlog pipeline in docs/project/
refactor-backlog-setup/ up to and including three of the seven Phase 1
agents.

00-baseline.md establishes the metrics every later agent must cite, using
only tooling already in the repo (vitest lcov, coverlet cobertura, ESLint's
core `complexity` rule at threshold 0 for a full distribution, depcruise
--metrics). Duplication and C# complexity had no tooling, so
tools/baseline-scan.mjs adds a deterministic ~200-line text scan rather
than a new dependency; the approximations are labelled as such.

Headline: FE 75.1% line coverage but only over the 98 of 220 source files a
spec loads; BE 97.6% line / 79.6% branch; 0 layering violations; 7.1%
duplication; 25 of 2085 TS functions over CC 10.

Then 02-testability, 04-cqrs-light and 06-adr-conformance (27 findings).
01/03/05 were skipped deliberately — the baseline shows little for them to
find; 07 (BIO2) and 08 (consolidation) are still open.

Each agent corrected a baseline observation of mine, and in every case the
error was in something derived rather than measured:

- BL-007 counted ~13 adapter "mutations" from the `runSubmit` helper name;
  5 of those call sites are reads. It also missed 3 real mutations that
  reach the raw ApiClient and never return a Result.
- BL-002 diagnosed the 100%-duplicated auth folders as ADR-0002's
  divergence prediction failing. It never had a chance to fail: §3's
  `Principal` union was never built.
- BL-004 named libs/shared/domain and libs/beheer/contracts as coverage
  gaps; both are pure type declarations where 0% is unimprovable.

All three corrections are recorded inline in 00-baseline.md §10, so agent
08 does not inherit the bad numbers.

.prettierignore excludes the agent prompt directories — reflowing their
markdown would edit the prompt text itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 16:44:32 +02:00

548 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Scope: apps/ssp (6 contexts), apps/behandelportal (3 contexts), libs/shared (11 layers), libs/beheer (5 layers), backend/src/BigRegister.Api (6 folders), backend/tests
## Status: complete
## Last updated: 2026-08-26
## Depends on: none
## ---
# 00 — Metrics Baseline
Fixed input to every Phase 1 agent (01–07). **No agent may propose a change without
citing a `BL-###` observation or a metric row from this file.**
---
## 1. Method, and what to trust
Everything was measured with tooling already in the repo. No dependency was added, no
config file edited.
| Metric | Command | Trust |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| FE coverage | `npm run test:coverage` → `coverage/{ssp,behandelportal,shared,beheer}/lcov.info` | exact |
| FE spec reach | derived: source files present in any lcov vs. files on disk | exact |
| BE coverage | `cd backend && dotnet test BigRegister.slnx --filter "Category!=Integration" --collect:"XPlat Code Coverage"` | exact |
| TS complexity + fn length | `npx eslint apps libs --rule '{"complexity":["warn",0],"max-lines-per-function":["warn",{"max":0,"skipBlankLines":true,"skipComments":true}]}' -f json` — threshold 0 makes ESLint report _every_ function with its score, giving a full distribution rather than only violations | exact (ESLint core rule, no plugin) |
| C# complexity | `node docs/project/refactor-backlog-setup/refactor-backlog/tools/baseline-scan.mjs --complexity` | **file CC exact-ish; per-method CC approximate** |
| Duplication | `node …/tools/baseline-scan.mjs --dup` | **approximate** |
| Layering violations | `npm run dep:check` | exact |
| Coupling / instability | `npx depcruise apps/<app>/src libs --config .dependency-cruiser.<app>.js --metrics --output-type metrics` | exact |
`tools/baseline-scan.mjs` is new and lives in this workspace so the numbers reproduce.
It is a crude text scan: line-window hashing for duplication (not token-based like
jscpd), regex method detection for C# (does not understand expression-bodied members or
nested lambdas). Deterministic, zero-install, good enough to answer "did this move".
Upgrade path if a ticket ever needs more precision: jscpd, and a Roslyn analyzer.
**Excluded as generated** (counted once, then excluded everywhere):
`libs/shared/src/infrastructure/api-client.ts` (2372 lines, NSwag) and
`backend/.../Data/Migrations/**` (17 files, 2690 lines, EF Core).
**LCOV aggregation rule.** The four FE project runs overlap — `libs/shared` files appear
in `ssp`'s lcov as well as `shared`'s. Each file is attributed to its owning module and
the **best** result across the four runs is taken; a shared file exercised by an app's
specs is genuinely covered.
**Protocol deviation, stated for the record.** The measurement runs are repo-global (one
coverage run, one depcruise run) and cannot be executed module-by-module; only the
write-up is per module. The resume contract still holds — the file is the recovery state.
---
## 2. Size inventory
| Unit | src .ts/.cs | src lines | spec files | spec lines | stories |
| ------------------------------ | ----------: | ---------: | ---------: | ---------: | ------: |
| apps/ssp | 92 | 10 415 | 39 | 3 564 | 21 |
| apps/behandelportal | 29 | 1 309 | 7 | 396 | 4 |
| libs/shared (excl. generated) | 86 | 5 194 | 21 | 1 226 | 40 |
| libs/beheer | 13 | 1 146 | 4 | 269 | 1 |
| **Frontend total** | **220** | **18 064** | **71** | **5 455** | **66** |
| backend/src (excl. Migrations) | 54 | 4 989 | — | — | — |
| backend/tests | 39 | — | 39 | 4 253 | — |
| e2e | 5 | 332 | — | — | — |
Backend source by folder: `Program.cs` 940 · `Data` (excl. Migrations) 1 697 · `Domain`
991 · `Zgw` 710 · `Contracts` 329 · `Stamdata` 322. Zero `.html` template files exist
anywhere in `apps/`/`libs/` — every Angular template is inline.
---
## 3. Coverage
### 3a. Frontend — line/branch coverage of files a spec actually reaches
| Module | Files | Lines | Line % | Branches | Branch % |
| -------------------------- | -----: | --------: | --------: | --------: | --------: |
| ssp/auth | 4 | 35 | 42.9% | 26 | 46.2% |
| bhp/auth | 4 | 35 | 42.9% | 26 | 46.2% |
| libs/shared/upload | 2 | 125 | 52.0% | 118 | 50.0% |
| libs/beheer/application | 1 | 70 | 65.7% | 37 | 40.5% |
| libs/beheer/infrastructure | 1 | 37 | 67.6% | 43 | 60.5% |
| ssp/herregistratie | 5 | 302 | 70.9% | 286 | 67.8% |
| libs/shared/layout | 2 | 80 | 72.5% | 36 | 55.6% |
| libs/shared/ui | 13 | 279 | 72.8% | 230 | 75.2% |
| ssp/brief | 11 | 486 | 75.3% | 461 | 68.8% |
| ssp/registratie | 21 | 430 | 80.0% | 471 | 77.3% |
| libs/shared/application | 8 | 76 | 80.3% | 50 | 70.0% |
| bhp/behandeling | 5 | 107 | 91.6% | 146 | 81.5% |
| libs/shared/infrastructure | 9 | 75 | 94.7% | 84 | 81.0% |
| libs/shared/kernel | 5 | 28 | 96.4% | 20 | 90.0% |
| libs/beheer/domain | 2 | 54 | 98.1% | 64 | 75.0% |
| ssp/showcase | 1 | 11 | 100.0% | 4 | 100.0% |
| libs/shared/testing | 3 | 9 | 100.0% | 2 | 50.0% |
| **TOTAL** | **98** | **2 240** | **75.1%** | **2 104** | **70.6%** |
### 3b. Frontend — spec _reach_ (the number that matters)
75.1% is coverage **of the 98 files a spec imports**. It is not coverage of the codebase.
122 of 220 source files are never loaded by any Vitest run at all.
| Module | Source files | Reached | Never reached | % reached |
| ---------------------------------------- | -----------: | ------: | ------------: | --------: |
| ssp/root, ssp/shell, bhp/root, bhp/shell | 12 | 0 | 12 | 0% |
| libs/shared/domain | 3 | 0 | 3 | 0% |
| libs/beheer/ui | 4 | 0 | 4 | 0% |
| libs/beheer/contracts | 1 | 0 | 1 | 0% |
| libs/shared/layout | 11 | 2 | 9 | 18% |
| bhp/behandeling | 16 | 5 | 11 | 31% |
| ssp/showcase | 3 | 1 | 2 | 33% |
| libs/shared/ui | 34 | 13 | 21 | 38% |
| ssp/brief | 26 | 11 | 15 | 42% |
| bhp/auth | 8 | 4 | 4 | 50% |
| libs/shared/upload | 4 | 2 | 2 | 50% |
| ssp/registratie | 41 | 21 | 20 | 51% |
| ssp/herregistratie | 9 | 5 | 4 | 56% |
| ssp/auth | 6 | 4 | 2 | 67% |
| libs/beheer/domain | 3 | 2 | 1 | 67% |
| libs/shared/application | 11 | 8 | 3 | 73% |
| libs/shared/infrastructure | 11 | 9 | 2 | 82% |
| libs/shared/kernel | 5 | 5 | 0 | 100% |
| **TOTAL** | **220** | **98** | **122** | **45%** |
Most of the 122 are `ui/` components, which CLAUDE.md §5 deliberately exercises through
**Storybook + the a11y addon (66 stories)**, not Vitest. That is a house decision, not a
gap — see BL-004 before filing anything against it.
### 3c. Backend
| Module | Files | Lines | Line % | Branches | Branch % |
| ------------------ | -----: | --------: | --------: | --------: | --------: |
| backend/Domain | 17 | 730 | 94.2% | 356 | 82.0% |
| backend/Stamdata | 10 | 252 | 96.8% | 120 | 71.7% |
| backend/Program.cs | 1 | 1 160 | 97.4% | 316 | 84.8% |
| backend/Contracts | 2 | 334 | 97.6% | 40 | 65.0% |
| backend/Zgw | 8 | 624 | 98.1% | 124 | 85.5% |
| backend/Data | 13 | 1 852 | 99.0% | 400 | 75.5% |
| **TOTAL** | **51** | **4 952** | **97.6%** | **1 356** | **79.6%** |
241 tests, all green. Every backend source file is reached. Branch coverage is the weak
axis, not line coverage — `Contracts` 65.0%, `Stamdata` 71.7%, `Data` 75.5%.
---
## 4. Complexity
### 4a. TypeScript (exact — ESLint core `complexity`)
Distribution over 2 085 source functions (specs/stories excluded):
**p50 1 · p75 2 · p90 3 · p95 5 · p99 12 · max 27.** 25 functions exceed CC 10 (1.2%).
Function length over 1 149 functions: **p50 3 · p75 7 · p90 13 · p95 20 · p99 34 ·
max 143.** Only 2 functions exceed 75 lines.
| Module | Fns | max CC | p90 CC | CC>10 | max fn lines | fn>75 |
| -------------------------------------------------------- | --: | -----: | -----: | ----: | -----------: | ----: |
| libs/shared/upload | 90 | 27 | 4 | 1 | 109 | 1 |
| ssp/registratie | 361 | 23 | 4 | 7 | 143 | 1 |
| ssp/herregistratie | 144 | 19 | 3 | 4 | 31 | 0 |
| libs/shared/infrastructure | 32 | 19 | 6 | 1 | 33 | 0 |
| ssp/brief | 608 | 17 | 3 | 6 | 73 | 0 |
| bhp/behandeling | 85 | 16 | 5 | 3 | 28 | 0 |
| libs/beheer/domain | 22 | 11 | 7 | 1 | 24 | 0 |
| libs/beheer/infrastructure | 13 | 11 | 7 | 1 | 25 | 0 |
| libs/shared/ui | 285 | 11 | 3 | 1 | 37 | 0 |
| libs/beheer/ui | 100 | 10 | 1 | 0 | 8 | 0 |
| libs/shared/application | 61 | 7 | 3 | 0 | 30 | 0 |
| bhp/auth · ssp/auth · ssp/shell | 63 | 5 | 3 | 0 | 13 | 0 |
| libs/beheer/application | 42 | 4 | 3 | 0 | 15 | 0 |
| libs/shared/kernel | 18 | 4 | 4 | 0 | 13 | 0 |
| libs/shared/layout | 75 | 4 | 2 | 0 | 29 | 0 |
| ssp/showcase · ssp/root · bhp/root · libs/shared/testing | 86 | 3 | 1 | 0 | 15 | 0 |
**Read BL-001 before filing any complexity ticket.** 23 of the 25 CC>10 functions are
reducers (9), `parse*` trust boundaries (10), or `validate*` (4) — all three are
mandated house idioms.
The 25, in full:
| CC | Function | Location | Kind |
| --: | ------------------------ | -------------------------------------------------------------------------------- | --------- |
| 27 | `reduceUpload` | libs/shared/src/upload/upload.machine.ts:131 | reducer |
| 23 | `validateStep` | apps/ssp/src/app/registratie/domain/registratie-wizard.machine.ts:117 | validate |
| 20 | `reduce` | apps/ssp/src/app/registratie/domain/registratie-wizard.machine.ts:321 | reducer |
| 19 | `validateStep` | apps/ssp/src/app/herregistratie/domain/intake.machine.ts:97 | validate |
| 19 | `parseAanvraagStatus` | apps/ssp/src/app/registratie/infrastructure/applications.adapter.ts:72 | parse |
| 19 | `fetch` | libs/shared/src/infrastructure/api-client.provider.ts:49 | **other** |
| 17 | `parseOrgTemplate` | apps/ssp/src/app/brief/infrastructure/brief.adapter.ts:331 | parse |
| 17 | `parseDuoLookup` | apps/ssp/src/app/registratie/infrastructure/duo.adapter.ts:46 | parse |
| 16 | `parseBeoordelingStatus` | apps/behandelportal/src/app/behandeling/infrastructure/beoordeling.adapter.ts:31 | parse |
| 16 | `parseStatus` | apps/ssp/src/app/brief/infrastructure/brief.adapter.ts:202 | parse |
| 16 | `validateAll` | apps/ssp/src/app/registratie/domain/registratie-wizard.machine.ts:166 | validate |
| 15 | `parseBeoordelingView` | apps/behandelportal/src/app/behandeling/infrastructure/beoordeling.adapter.ts:68 | parse |
| 15 | `validateAll` | apps/ssp/src/app/herregistratie/domain/intake.machine.ts:135 | validate |
| 14 | `reduce` | apps/behandelportal/src/app/behandeling/domain/besluit.machine.ts:70 | reducer |
| 14 | `reduce` | apps/ssp/src/app/brief/domain/org-template.machine.ts:57 | reducer |
| 14 | `reduce` | apps/ssp/src/app/herregistratie/domain/herregistratie.machine.ts:161 | reducer |
| 14 | `reduce` | apps/ssp/src/app/herregistratie/domain/intake.machine.ts:222 | reducer |
| 14 | `reduce` | apps/ssp/src/app/registratie/domain/change-request.machine.ts:57 | reducer |
| 13 | `reduce` | apps/ssp/src/app/brief/domain/brief.machine.ts:188 | reducer |
| 12 | `parsePassage` | apps/ssp/src/app/brief/infrastructure/brief.adapter.ts:235 | parse |
| 12 | `parseBrief` | apps/ssp/src/app/brief/infrastructure/brief.adapter.ts:277 | parse |
| 12 | `parseDashboardView` | apps/ssp/src/app/registratie/infrastructure/dashboard-view.adapter.ts:50 | parse |
| 11 | `reduce` | libs/beheer/src/domain/stamdata-editor.machine.ts:31 | reducer |
| 11 | `parseAuditEntries` | libs/beheer/src/infrastructure/audit.adapter.ts:21 | parse |
| 11 | `collect` | libs/shared/src/ui/rich-text-editor/rich-text-dom.ts:130 | **other** |
Functions over 75 lines — the entire population:
`createDraftSync` 143 lines (apps/ssp/src/app/registratie/application/draft-sync.ts:50),
`reduceUpload` 109 lines (libs/shared/src/upload/upload.machine.ts:131).
### 4b. C# (approximate)
| Module | Files | Σ file CC | max file CC | Methods | max method CC | p90 | CC>10 |
| ------------------ | ----: | --------: | ----------: | ------: | ------------: | --: | ----: |
| backend/Program.cs | 1 | **78** | **78** | 13 | 12 | 7 | 1 |
| backend/Data | 23 | 101 | 27 | 77 | 16 | 4 | 1 |
| backend/tests | 39 | 121 | 26 | 230 | 20 | 2 | 1 |
| backend/Domain | 18 | 71 | 21 | 36 | 8 | 6 | 0 |
| backend/Stamdata | 10 | 34 | 21 | 11 | 10 | 6 | 0 |
| backend/Zgw | 8 | 39 | 11 | 20 | 8 | 4 | 0 |
| backend/Contracts | 2 | 4 | 3 | 6 | 3 | 3 | 0 |
Method-length distribution (n=393): p50 9 · p90 22 · p99 90 · max 293.
Highest-CC files: `Program.cs` 78 · `Data/ApplicationStore.cs` 27 ·
`tests/OpenZaakZaakSourceTests.cs` 26 · `Domain/Letters/LetterHtml.cs` 21 ·
`Stamdata/StamdataTable.cs` 21 · `Data/BriefStore.cs` 17.
Methods over CC 10: `ToDto` (Data/BriefStore.cs:34, CC 16),
`LogBrief` (Program.cs:889, CC 12), and one 293-line test method
(`CreateZaak_posts_zaak_status_and_rol_and_maps_the_result_back`, CC 20).
---
## 5. Duplication (approximate — 6-line normalized window)
Repo-wide: **1 755 of 24 701 significant lines duplicated = 7.1%.**
| Module | Sig. lines | Duplicated | % |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------: | ---------: | ---------: |
| ssp/auth | 211 | 211 | **100.0%** |
| bhp/auth | 243 | 211 | **86.8%** |
| bhp/root | 90 | 68 | 75.6% |
| ssp/root | 122 | 68 | 55.7% |
| bhp/shell | 21 | 8 | 38.1% |
| ssp/herregistratie | 1 536 | 121 | 7.9% |
| ssp/brief | 5 015 | 393 | 7.8% |
| backend/tests | 2 538 | 197 | 7.8% |
| backend/Data | 1 068 | 82 | 7.7% |
| libs/shared/infrastructure | 510 | 36 | 7.1% |
| libs/beheer/domain | 250 | 17 | 6.8% |
| libs/beheer/ui | 509 | 34 | 6.7% |
| libs/beheer/application | 185 | 12 | 6.5% |
| bhp/behandeling | 954 | 55 | 5.8% |
| ssp/shell | 194 | 8 | 4.1% |
| ssp/registratie | 3 798 | 144 | 3.8% |
| libs/shared/ui | 2 604 | 58 | 2.2% |
| backend/Zgw | 327 | 6 | 1.8% |
| ssp/showcase | 487 | 8 | 1.6% |
| backend/Program.cs | 510 | 6 | 1.2% |
| libs/shared/application | 566 | 6 | 1.1% |
| libs/shared/layout | 869 | 6 | 0.7% |
| backend/Contracts, backend/Domain, backend/Stamdata, libs/shared/{domain,kernel,upload,testing,environments}, libs/beheer/{contracts,infrastructure}, e2e | 1 894 | 0 | 0.0% |
Top clone pairs:
| Windows | Pair |
| ------: | ------------------------------------------------------------------------------------------------ |
| 39 | `bhp/auth/application/session.store.ts` ↔ `ssp/auth/application/session.store.ts` |
| 36 | `bhp/auth/auth.guard.spec.ts` ↔ `ssp/auth/auth.guard.spec.ts` |
| 35 | `bhp/auth/ui/login-form/…` ↔ `ssp/auth/ui/login-form/…` |
| 25 | `bhp/app.config.ts` ↔ `ssp/app.config.ts` |
| 23 | `bhp/auth/ui/login.page.ts` ↔ `ssp/auth/ui/login.page.ts` |
| 21 | `bhp/auth/auth.guard.ts` ↔ `ssp/auth/auth.guard.ts` |
| 13 | `libs/shared/…/role.interceptor.spec.ts` ↔ `…/subject.interceptor.spec.ts` |
| 11 | `brief/ui/letter-canvas.stories.ts` ↔ `brief/ui/letter-composer.stories.ts` |
| 9 | `ssp/brief/ui/org-template.page.ts` ↔ `libs/beheer/src/ui/stamdata.page.ts` |
| 8 | `bhp/behandeling/domain/besluit.machine.ts` ↔ `ssp/registratie/domain/change-request.machine.ts` |
| 8 | `tests/OpenZaakZaakSourceTests.cs` ↔ `tests/ZgwDivergenceTests.cs` |
| 7 | `bhp/…/besluit-form.component.ts` ↔ `ssp/…/change-request-form.component.ts` |
| 7 | `ssp/…/intake-wizard.component.ts` ↔ `ssp/…/registratie-wizard.component.ts` |
**The auth duplication is a deliberate decision, not an accident** — see BL-002 before
proposing to merge it.
---
## 6. Layering and coupling
`npm run dep:check`: **0 violations** (223 modules, 584 dependencies cruised) across 11
`severity: error` rules — `shared-no-features`, `beheer-no-features`, `shared-no-beheer`,
`<app>-no-other-app`, per-context `<app>-<context>-scope`, `domain-is-pure`,
`contracts-import-nothing`, `ui-not-infrastructure`, `apiclient-infrastructure-only`,
`no-testing-in-production`, `no-circular`.
Instability I = Ce/(Ca+Ce). Low I = stable base, high I = volatile leaf. Measured in the
ssp cruise (behandelportal's figures for `libs/*` differ only in Ca, same shape):
| Folder | N | Ca | Ce | I |
| --------------------------------------- | --: | --: | --: | -----: |
| apps/ssp/src/app/brief | 46 | 2 | 143 | 99% |
| apps/ssp/src/app/herregistratie | 18 | 4 | 80 | 95% |
| apps/ssp/src/app/showcase | 4 | 1 | 17 | 94% |
| apps/ssp/src/app/registratie | 67 | 19 | 181 | 91% |
| apps/behandelportal/src/app/behandeling | 24 | 2 | 62 | 97% |
| apps/behandelportal/src/app/auth | 11 | 4 | 25 | 86% |
| apps/ssp/src/app/auth | 9 | 5 | 24 | 83% |
| libs/beheer/src/ui | 5 | 3 | 27 | 90% |
| libs/beheer/src/application | 3 | 2 | 16 | 89% |
| libs/beheer/src/infrastructure | 3 | 3 | 11 | 79% |
| libs/shared/src/layout | 19 | 22 | 52 | 70% |
| libs/shared/src/ui | 68 | 128 | 111 | 46% |
| libs/shared/src/infrastructure | 19 | 43 | 25 | 37% |
| libs/shared/src/application | 18 | 55 | 30 | 35% |
| libs/shared/src/upload | 5 | 30 | 13 | 30% |
| libs/beheer/src/domain | 5 | 8 | 3 | 27% |
| libs/shared/src/testing | 4 | 17 | 2 | 11% |
| libs/shared/src/kernel | 9 | 71 | 4 | **5%** |
| libs/shared/src/domain | 3 | 11 | 0 | 0% |
| libs/beheer/src/contracts | 1 | 0 | 0 | 0% |
This is textbook: `kernel`/`domain` are the stable base (I ≤ 5%), feature contexts are
volatile leaves (I ≥ 83%), nothing depends on them. **The frontend dependency structure
is not a problem area** — do not spend tickets here.
**The backend has no equivalent enforcement at all.** `Domain/` purity (verified: zero
`Microsoft.EntityFrameworkCore` / `Microsoft.AspNetCore` imports) holds by convention and
code review only. See BL-006.
---
## 7. Pattern inventory — what already exists
Agents 03 (DDD/hexagonal) and 04 (CQRS-light) may only **extend** what is listed here.
They may not introduce either pattern into a module where it is absent.
### Backend
| Pattern | State |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Ports with 2+ implementations | `IZaakSource` (`Data/LocalZaakSource.cs` ↔ `Zgw/OpenZaakZaakSource.cs`), `IDocumentSource` (`Data/LocalDocumentSource.cs` ↔ `Zgw/OpenZaakDocumentSource.cs`) — config-switched on `Zgw:Enabled` at `Program.cs:57-84`, ADR-0005 |
| Single-impl interface | `IIdentityProvider` → `StubIdentityProvider` |
| **Not behind any port** | 7 static, non-DI stores: `ApplicationStore`, `DocumentStore`, `BriefStore`, `OrgTemplateStore`, `FeatureFlagStore`, `AuthzAuditStore`, `IdempotencyStore`. No `AddDbContext`; each opens a short-lived context via `Db.Create()` under its own lock. Deliberate, documented in `Data/Db.cs` and `Program.cs:40-45` |
| Domain purity | `Domain/` is EF-free and ASP-free (verified). Rules are static classes of pure functions with co-located tests in `tests/Domain/`: `SubmissionRules`, `DocumentRules`, `IntakePolicy`, `BeoordelingRules`, `DiplomaRules`, `HerregistratieRule`, `LetterHtml`, `OrgTemplateRules`, `Authz`, `FeatureFlags`. `Domain/Applications/Aanvraag.cs` is a C# tagged union (`Concept`/`Submitted`/`Decided`) |
| CQRS-light | **Partial.** `Contracts/Dtos.cs` holds 65 records split by direction (`*Request` in, `*Dto`/`*Response` out). Read/write split exists as _comment banners_ inside a single **940-line `Program.cs`** carrying all 48 endpoint mappings. No handler types, no mediator, no `Features/` folders. Cross-cutting behaviour is factored into local helpers (`Submit`, `StamdataAdmin`, `CasesAdmin`, `Beoordelen`, `OrgAdmin`, `FlagsAdmin`) — authorization/idempotency wrappers, not handlers |
| Mapping | `Contracts/Mappers.cs` (`.ToDto()`, `.ToDetailDto()`), `Data/AanvraagMapper.cs` |
| ZGW anti-corruption layer | Fully built: `Zgw/{OpenZaakZaakSource,OpenZaakDocumentSource,ZgwHttpClient,ZgwTokenProvider,ZgwZaakMapper,ZgwOptions,ZgwDiagnosticHandler}.cs`, 5 test files. ADR-0005 |
### Frontend
| Pattern | Count | Notes |
| ------------------------------------- | ----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Command factories (write side) | 3 | `registratie/application/submit-change-request.ts`, `behandeling/application/submit-besluit.ts`, `registratie/application/draft-sync.ts` |
| Mutations living _inline in adapters_ | ~13 | `brief.adapter.ts` (save/submit/approve/reject/send/reset), `org-template.adapter.ts` (save/publish/rollback), `stamdata.adapter.ts` — they call `runSubmit` directly instead of going through a command factory |
| Infrastructure adapters (read side) | 20 | +1 outside an `infrastructure/` folder: `libs/shared/src/upload/upload.adapter.ts` |
| `parse*` trust boundaries | 30 | 24 in adapters, 6 in value objects / kernel |
| Application stores | 15 | all `providedIn: 'root'` |
| Elm-style machines | 9 | 8 under a `domain/` folder; outlier `libs/shared/src/upload/upload.machine.ts` |
| Explicit port | 1 | `SessionPort` + `SESSION_PORT` token (`libs/shared/src/application/session.port.ts`) |
| Config-seam tokens | 3 | `DEBUG_PANEL`, `HEADER_NAV_ITEMS`, `HEADER_ADMIN_LINKS` |
| `contracts/` DTO files | 4 | most adapters consume NSwag-generated types directly instead |
Shared application kit (`libs/shared/src/application/`): `remote-data.ts`, `store.ts`,
`submit.ts`, `action-state.ts`, `debounced-save.ts`, `history.ts`,
`machine-remote-data.ts`, `pending-saves.ts`.
### ADRs on record
`0001` BFF-lite + decision DTOs (Accepted) · `0002` user groups as actors, not bounded
contexts (was **Proposed** when measured; promoted to **Accepted** 2026-08-26 by ADR-C-005,
with §3's `Principal` omission recorded in the ADR as known debt) · `0003` CIBG Huisstijl
(Accepted) · `0004`
stamdata as code (Accepted) · `0005` OpenZaak behind the BFF — the explicit
ports-and-adapters ADR (Accepted) · `0006` test data through the production door
(Accepted).
---
## 8. Rankings, worst to best
**Spec reach (FE):** ssp/root · ssp/shell · bhp/root · bhp/shell · libs/shared/domain ·
libs/beheer/ui · libs/beheer/contracts (all 0%) → libs/shared/layout 18% →
bhp/behandeling 31% → libs/shared/ui 38% → ssp/brief 42% → … → libs/shared/kernel 100%.
**Line coverage (FE, of reached files):** ssp/auth · bhp/auth 42.9% →
libs/shared/upload 52.0% → libs/beheer/application 65.7% → … → ssp/showcase 100%.
**Branch coverage (BE):** Contracts 65.0% → Stamdata 71.7% → Data 75.5% → Domain 82.0% →
Program.cs 84.8% → Zgw 85.5%.
**Duplication:** ssp/auth 100% → bhp/auth 86.8% → bhp/root 75.6% → ssp/root 55.7% →
bhp/shell 38.1% → ssp/herregistratie 7.9% → … → 12 modules at 0%.
**Complexity (TS, CC>10 count):** ssp/registratie 7 → ssp/brief 6 → ssp/herregistratie 4
→ bhp/behandeling 3 → 5 modules with 1 → the rest 0.
**Complexity (C#, file CC):** Program.cs 78 → Data 101 across 23 files (max 27) →
Domain 71 (max 21) → Zgw 39 → Stamdata 34 → Contracts 4.
**Coupling:** nothing to rank — 0 violations, healthy instability gradient (§6).
---
## 9. Thresholds Phase 1 must use
Derived from the measured distributions above, not invented.
| Threshold | Value | Basis |
| ---------------------------------------- | ----------------------------------------- | -------------------------------------------------------------------- |
| Cyclomatic complexity | **> 10** | fixed by the agent prompt; TS p99 is 12, so this catches the top ~1% |
| **Agent 01's `[N] lines` — TS function** | **> 40** | TS fn-length p99 is 34; only 4 functions exceed 50 |
| Agent 01's `[N] lines` — C# method | **> 60** | C# method p99 is 90, p90 is 22 |
| Agent 01's `[N] lines` — component/file | **> 400** | 6 TS files and 1 C# file exceed it |
| Nesting depth | **> 3** | fixed by the agent prompt |
| Duplication worth a ticket | **> 10% of a module's significant lines** | repo mean is 7.1% |
Largest files (the >400 population): `backend/Program.cs` 940 ·
`registratie/ui/registratie-wizard.component.ts` 645 · `showcase/concepts.page.ts` 496 ·
`brief/ui/letter-canvas.component.ts` 463 · `brief/infrastructure/brief.adapter.ts` 437 ·
`herregistratie/ui/intake-wizard.component.ts` 406.
---
## 10. Baseline observations (cite these)
Stable IDs. A Phase 1 finding must reference one of these or a metric row above.
**BL-001 — 23 of 25 TS functions over CC 10 are mandated house idioms.**
Reducers (9), `parse*` trust boundaries (10) and `validate*` (4) are switch/guard-dense
by construction: CLAUDE.md §3 requires a tagged-union reducer over booleans, and ADR-0001
requires a hand-written `parse*` at every wire boundary. High CC there is the design
working, not decaying. Only two CC>10 functions are outside the idiom:
`api-client.provider.ts:49 fetch` (CC 19) and `rich-text-dom.ts:130 collect` (CC 11).
_Any complexity ticket against a `reduce*`/`parse*`/`validate*` must argue why this case
differs — a bare CC number is not sufficient grounds._
**BL-002 — `ssp/auth` is 100% duplicated against `bhp/auth`, deliberately.**
211 of 211 significant lines; `session.store.ts`, `login-form.component.ts`, `login.page.ts`
and `auth.guard.ts` are near-identical. CLAUDE.md §1 and ADR-0002 state auth is _not_
shared because Zorgverlener and Medewerker are different `Principal` variants expected to
diverge. **The divergence has not happened yet.** This is legitimately in scope for
agent 06 (ADR conformance) as either an `ADR-fix` — the prediction has not held over two
phases of work — or a "still waiting" note. It is _not_ a straightforward
extract-to-shared refactor; that would contradict an accepted ADR.
> **Sharpened 2026-08-26 by agent 06 (ADR-C-004/005) — verified.** "The prediction has
> not held" is the wrong diagnosis. ADR-0002 §3's concrete deliverable, `Session →
Principal`, was **never built**: `grep -rn "Principal" apps libs` returns exactly one
> hit, a comment in `libs/shared/src/infrastructure/role.ts:8`, and no type. `diff -rq`
> over the two auth folders shows **zero** content differences — 9 of 11 files identical,
> the only delta being two extra files in behandelportal. `behandelportal`'s Behandelaar
> still carries a `bsn` and `login.page.ts:31` logs a backoffice user in through DigiD.
> The rule was not falsified, it was untested; the divergence that did occur went through
> an orthogonal side door (`medewerker.interceptor.ts`) that never touches `Session`.
> Amending ADR-0002 would ratify the omission. Use agent 06's sequencing instead.
**BL-003 — `Program.cs` is the single largest complexity concentration in the repo.**
940 lines, 48 endpoint mappings, file CC 78 (next-highest file: 27), read/write separated
only by comment banner. 97.4% line / 84.8% branch covered, so it is well-tested, not
fragile — this is a structure finding, not a correctness one. It is the one place where
agent 04's CQRS-light and agent 03's vertical-slice thinking both have real purchase,
_and_ the one place where "extend the existing pattern, don't introduce one" is hardest to
honour: there is no `Features/` folder to extend.
**BL-004 — 122 of 220 FE source files (55%) are never loaded by any Vitest run.**
Overwhelmingly `ui/` components, which CLAUDE.md §5 covers via 66 Storybook stories with
the a11y addon instead. Agent 02 must distinguish _"untested"_ from _"tested through
Storybook"_ before filing; the genuine gaps are non-`ui/` files with no spec —
`libs/shared/domain` (3 files, 0%), `libs/beheer/contracts`, and the app root/shell files.
> **Corrected 2026-08-26 by agent 02 — verified.** The two named "genuine gaps" are false
> positives. `libs/shared/src/domain` is 30 lines across 3 files — interfaces and type
> aliases plus a single string const — and `libs/beheer/src/contracts/stamdata.dto.ts` is
> 30 lines of DTO shapes. Neither contains an executable statement, so 0% is correct and
> unimprovable. Close both rows rather than ticketing them. Only the app root/shell files
> remain as candidates from this observation.
**BL-005 — backend branch coverage lags line coverage by 18 points** (97.6% vs 79.6%).
Weakest: `Contracts` 65.0%, `Stamdata` 71.7%, `Data` 75.5%. Line coverage is near-total,
so the missing tests are edge-case branches, not whole units.
**BL-006 — the backend has zero automated architecture enforcement.**
The frontend has 11 dependency-cruiser rules at `severity: error`, 0 violations, running
in CI. The backend has none: one assembly, no `Domain.csproj` boundary, no
NetArchTest/ArchUnitNET. `Domain/` purity currently holds by convention. Any agent-03
proposal that depends on the backend's layering staying clean should note that nothing
enforces it.
**BL-007 — the FE write side is inconsistently placed.**
3 command factories vs ~13 call sites invoking `runSubmit` directly inside adapters
(`brief.adapter.ts`, `org-template.adapter.ts`, `stamdata.adapter.ts`). CLAUDE.md §3 makes
the command factory the idiom. This is agent 04's clearest extend-an-existing-pattern
target — the pattern exists, it is just not applied uniformly.
> **Corrected 2026-08-26 by agent 04 (CQ-003/CQ-005) — verified.** The "~13 mutations"
> count was derived from the `runSubmit` helper name and is wrong: 5 of those call sites
> are **reads**, not writes. `libs/beheer/src/infrastructure/stamdata.adapter.ts` exposes
> only `list()` and `load()` and its own docstring says "Both endpoints are reads … There
> is no write method", yet both call `runSubmit`. Same at `brief.adapter.ts:56` and
> `org-template.adapter.ts:39,51`. `runSubmit` is the write fold — it mints the
> Idempotency-Key — so the name, not the code, produced the miscount. Conversely agent 04
> found **3 mutations this baseline missed entirely**: `ApplicationsStore.cancel`,
> `AdminCasesStore.delete`, `FeatureFlagStore.set` reach the raw `ApiClient` and never
> return a `Result`. Use agent 04's inventory, not this count.
**BL-008 — `coverageExclude` does not exclude the generated API client.**
`libs/shared/src/infrastructure/api-client.ts` is listed in `coverageExclude` in all four
`angular.json` test targets, yet appears in all four `lcov.info` files (987 lines at ~7%),
dragging the reported `libs/shared/infrastructure` figure from 94.7% down to 6.9%. This
file excludes it manually. Small, real, and cheap to fix.
**BL-009 — no coverage threshold is enforced anywhere.**
Neither `angular.json` nor CI sets a minimum; `npm run test:coverage` runs in CI and the
output is discarded. There is no ratchet, so no ticket can be verified as "improved
coverage" by CI alone — verify against the numbers in this file.
**BL-010 — `libs/shared/src/upload/` sits outside the layer convention.**
`upload.machine.ts` (CC 27, 109 lines — the highest in the repo) lives in its own
top-level folder rather than under `domain/`, and `upload.adapter.ts` outside
`infrastructure/`. It is carved out by name in the `apiclient-infrastructure-only`
dependency-cruiser rule, i.e. the exception is already encoded rather than resolved.
**BL-011 — the FE test suite is flaky under parallel load.**
`npm run test:coverage` failed on the first run with two `[vitest-pool] Timeout waiting
for worker to respond` errors in `libs/shared`, then passed 21/21 when re-run serially.
Not a code defect; relevant to any ticket whose acceptance is "CI green".
---
## 11. Reproducing this file
```bash
npm run test:coverage # if flaky, re-run the failing project alone
cd backend && dotnet test BigRegister.slnx --filter "Category!=Integration" \
--collect:"XPlat Code Coverage"
npx eslint apps libs --rule '{"complexity":["warn",0],"max-lines-per-function":["warn",{"max":0,"skipBlankLines":true,"skipComments":true}]}' -f json
node docs/project/refactor-backlog-setup/refactor-backlog/tools/baseline-scan.mjs
npm run dep:check
npx depcruise apps/ssp/src libs --config .dependency-cruiser.ssp.js --metrics --output-type metrics
npx depcruise apps/behandelportal/src libs --config .dependency-cruiser.behandelportal.js --metrics --output-type metrics
```
The `baseline-scan.mjs` figures are deterministic and must reproduce exactly. Coverage
figures move with the tests.