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>
548 lines
47 KiB
Markdown
548 lines
47 KiB
Markdown
## 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.
|