ehoandClaude Sonnet 5 fb7b531fdf chore: remove 22 abandoned agent worktrees (RD-15)
The repository carried 22 abandoned agent worktrees under
.claude/worktrees/, left behind by past agent runs. They are
gitignored, so they never reached a commit, but they stayed on disk
and every unqualified repository-wide grep or find walked all 22
copies of the source tree.

Measured before: 48,005 files under .claude/worktrees/, against 856
tracked in the repository. An unqualified search walked 56 times more
files than the repository contains.

The verification gate confirmed both safety conditions before removal:
all 22 worktree-agent-* branch tips were already ancestors of main, and
all 22 worktrees were clean (unmerged: 0, dirty: 0).

Removal steps:
- git worktree remove for each of the 22 worktrees (no rm -rf, so the
  registrations in .git/worktrees/ stay consistent)
- git branch -d for each worktree-agent-* branch (lowercase -d, so an
  unmerged branch would block deletion instead of being force-deleted)
- git worktree prune to clear administrative entries

Measured after: 0 files under .claude/worktrees/, .claude/ shrank from
4.7 GB to 72 KB. The 856 tracked files are unchanged. HEAD is unchanged
from before the removals. npm run ci exits 0.

This ticket changes no tracked source file. The diff is this ticket
file and the README row, because the work is entirely in gitignored
paths and local branch refs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 18:48:52 +02:00

BIG-register Portals — Atomic Design POC

A two-app Angular monorepo showing how atomic design plus domain-driven boundaries make a frontend cheap to build, reuse and extend. The domain is the BIG-register (the Dutch register of healthcare professionals, run by CIBG): a citizen self-service portal and a case-handler backoffice, sharing one design system and one backend.

It is styled with the CIBG Huisstijl (a customized Bootstrap 5.2 build, vendored — ADR-0003) and built around one idea: make illegal states unrepresentable — in the UI's async states, in the domain types, and in the tests.

Demo / POCno real login (DigiD is faked) and synthetic seed data. But the business rules and data are served by a real ASP.NET Core backend (backend/) through a generated typed client, so the BFF + DDD design is demonstrable rather than hand-waved. A system-font stack stands in for the licensed Rijksoverheid font, and a text wordmark for the logo.

New here? Run npm run storybook and open Foundations → Learning Path — a paced, hands-on three-day route through the codebase, written for a strong programmer who is new to frontend functional programming. For everything else, docs/README.md is the documentation index.


Quick start

Everything at once — API, both portals:

docker compose up
# self-service portal  → http://localhost:4200
# behandelportal       → http://localhost:4201
# API + Swagger        → http://localhost:5000/swagger

This does not start OpenZaak. The BFF uses a local, in-memory case store instead. To run against a real OpenZaak, see backend/openzaak/README.md.

Or run the pieces yourself:

npm install
npm start                     # self-service portal  → :4200 (proxies /api → backend)
npm run start:behandelportal  # case-handler portal  → :4201

# in another terminal:
cd backend && dotnet run --project src/BigRegister.Api   # API → :5000/swagger

npm run storybook             # the design system + Foundations curriculum
npm run e2e                   # Playwright — starts the backend and app itself

Self-service flow: Login → Dashboard → Mijn gegevens → Registreren → Herregistratie → Intake → Brief. Behandelportal: Login → Werkvoorraad → Beoordeling (approve / reject / ask for more). Admin pages (/beheer/*, /brief/huisstijl) need the admin role — see roles and access.


Where to find things

I want to… Go to
learn the codebase from scratch npm run storybookFoundations → Learning Path
find any document docs/README.md — the full index
understand the architecture ARCHITECTURE.md
know why a decision was made the ADRs — BFF-lite, contexts, huisstijl, stamdata, ZGW, test data
work on the backend / BFF backend/README.md
run OpenZaak locally backend/openzaak/README.md
see what shipped, or pick up work docs/project/backlog/README.md
build a feature the house way .claude/skills/ — invocable recipes (new-feature, form-machine, …)
know the import rules dependencies.md — enforced by dep:check
work on this repo as an AI agent CLAUDE.md

Repo map

Path What lives there
apps/ssp/ Zorgverlener self-service portal (:4200)
apps/behandelportal/ Behandelaar backoffice (:4201)
libs/shared/ Design system, kernel, generated API client, Storybook Foundations docs
libs/beheer/ Admin/stamdata context, used identically by both apps
backend/ ASP.NET Core BFF (.NET 10, EF Core/SQLite), OpenZaak seam
e2e/ Playwright specs
docs/ reference/ (how + why) and project/ (backlog, PRDs)
public/ Vendored CIBG Huisstijl + assets
scripts/ CI gate, drift checks, generators

Commands

npm run ci          # ← run this before pushing: the whole gate, exactly what CI runs
Task Command
Run npm start, npm run start:behandelportal, docker compose up
Test npm test, npm run e2e, npm run test-storybook (axe on every story)
Check npm run lint, typecheck, dep:check, check:tokens, check:seam
Build npm run build, npm run build-storybook
Generate npm run gen:api (typed client), npm run gen (plop: value object, form machine, context)
Docs npm run storybook, npm run dep:graph, npm run gen:behaviour-spec

npm run ci chains lint, typecheck, dependency boundaries, formatting, token and seam drift checks, all four test projects, both localized builds, the backend suite, and the generated-artifact drift gates. The full script list is in package.json; CLAUDE.md explains the traps.


Where atomic design pays off

Two orthogonal axes, and that is the point. On disk, code is grouped by ownership — bounded context, then layer (domain/application/infrastructure/ui/, dependencies pointing inward). In Storybook, the same components are grouped by atomic level — story titles put them under Design System/Atoms|Molecules|Organisms|Templates. So libs/shared/src/ui/ is a flat folder of 26 components, and the atomic ladder lives in the sidebar where you actually browse it. See Atomic design and Domain-driven design in Foundations.

Reuse is the payoff. button, form-field, async, page-shell, site-header appear on essentially every screen across both apps. Change one, every screen follows.

A new page is composition, not new components. The branching intake wizard — the most complex flow in the app — needed exactly one new atom (radio-group) and one new organism (intake-wizard). Everything else was already there.

Theming is one stylesheet and a token bridge. libs/shared/styles.scss maps the app's semantic --rhc-* vocabulary onto CIBG/--bs-* values, so components reference tokens, never colours. Re-point the bridge to re-theme both apps with no component changes (ADR-0003). npm run check:tokens fails the build on a hardcoded colour.


Try it: dev affordances

Append ?scenario= to any data page to force an async state — the states are mutually exclusive by construction, via the <app-async> molecule:

URL What you see
/dashboard real data
/dashboard?scenario=slow skeletons for ~2.5s, then data
/dashboard?scenario=loading the loading state, held open
/dashboard?scenario=empty "geen gegevens" empty state
/dashboard?scenario=error error message + Opnieuw proberen (retry)

Append ?role=drafter|approver|admin to switch the dev role stand-in and unlock the admin pages. Both toggles are dev-only — neither interceptor is wired into production builds.


Tech notes

  • Angular 22 — standalone components, signals, resource(), native control flow, view transitions. No NgRx: shared state is a root singleton store with a pure reducer.
  • Backend — ASP.NET Core (.NET 10), EF Core/SQLite for applications, documents, the brief and the audit trail; BRP/DUO reference data stays in-memory seeded. Screen-shaped ("BFF-lite") endpoints return server-computed decisions the frontend renders rather than recomputes (ADR-0001). Cases can be sourced from OpenZaak/ZGW behind the same seam (ADR-0005).
  • Typed client — NSwag-generated from the backend's OpenAPI doc (npm run gen:api); CI fails on drift.
  • Boundaries are enforced, not hoped fordep:check fails the build if domain/ imports Angular, a context imports upward, or an app reaches into the other app.
  • i18n — every user-facing string is $localize-wrapped with a stable id (source locale nl). ng build --localize builds both nl and en, and i18nMissingTranslation: "error" fails the build if a string ships untranslated.
  • Dependencies — the shipped bundle audits clean (npm audit --omit=dev: 0 vulnerabilities). .npmrc sets legacy-peer-deps=true because Storybook's peer range lags Angular 22. Never run npm audit fix --force — it downgrades Angular 22 → 21.

Deliberately out of scope

Real auth/DigiD, real BRP/DUO upstreams, a production-grade database, NgRx, licensed RO/Rijks fonts and logo. The i18n build seam is proven, but the en translation is demo-quality and locale is a build-time choice, not a runtime switch.

S
Description
No description provided
Readme
5.5 MiB
Languages
TypeScript 61.6%
C# 24.2%
MDX 6.7%
JavaScript 2.2%
Shell 1.9%
Other 3.4%