## 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//src libs --config .dependency-cruiser..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`, `-no-other-app`, per-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.