docs: archive the finished backlogs (RD-30)

Two backlog trees are complete: `docs/project/backlog/` (75 files, every
WP done) and `docs/project/refactor-backlog-setup/` (the arc before it).
Move both under `docs/project/archive/` with `git mv`, so history stays
intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them,
because it points at the now-archived backlog README.

Add `docs/project/archive/README.md`. It states that these trees are
historical and names the two directories that are still live.

Repoint every inbound reference named in RD-30's Files table: CLAUDE.md,
the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the
`document-feature` and `new-ssp` skills, and the readable-codebase PLAN,
README, and RD-19 ticket. Fix two upward-relative links inside the moved
WP files (WP-68, WP-69) that gained a directory level and would otherwise
break. Repoint `.prettierignore`'s two agent-prompt exclusions to their
new path, so prettier keeps leaving those files' exact wording alone.

Mark RD-30 done and check off its acceptance criteria; flip its README
row to done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-08 23:00:38 +02:00
co-authored by Claude Opus 5
parent 097e8468e0
commit 12f17d9d73
161 changed files with 154 additions and 24 deletions
@@ -0,0 +1,137 @@
# WP-67 — Merge behandelportal into this repo as a monorepo
Status: done
Phase: 11 — Behandelportal
## Why
WP-61 bootstrapped `behandelportal` as a **separate sibling repo**, per ADR-0002's original
"separate frontend application" reading taken literally as "separate git repository." That
produced real, measured friction: a hand-vendored, manually-kept-in-sync copy of the
backend's OpenAPI doc instead of a live-generated one; `shared/ui`+`shared/layout` forked at
WP-61 and already silently diverging by the time this WP checked (7 files differed); a
`beheer` (admin/stamdata) context and the `styles.scss` token bridge duplicated byte-for-byte
across both repos; a second CI/lint/CLAUDE.md to hand-maintain. The user asked to collapse
this into one repo so the two apps share one CI, one shared UI library, and one generated API
client — the standard monorepo payoff, now that a second real frontend exists.
## Read first
- [ADR-0002](../reference/architecture/0002-user-groups-and-bounded-contexts.md) — its
"Amendment (WP-67)" section records exactly what changed and why the underlying
actor/bounded-context reasoning didn't.
- [dependencies.md](../reference/architecture/dependencies.md) — the per-app
dependency-cruiser split this WP introduced.
- `behandelportal-bootstrap` memory (prior sessions) for WP-61/62's own decisions.
## Decisions (made during this WP, not pre-made — see the plan file for the questions asked)
- **Unify** `libs/shared` into one library both apps import (not two copies) — the user's
explicit call, given the two trees had already started diverging.
- Restructure into **`apps/ssp/` + `apps/behandelportal/`** (not an Angular-CLI `projects/`
addition next to an untouched `src/app/`) — the user's call, accepting the larger diff.
- **Leave** the old sibling repo (`/home/eho/repos/behandelportal`) untouched — no deletion.
- Bring the content in as a **fresh commit**, no git-history import — that repo is itself a
fork of this repo's own pre-WP-61 history, so a subtree merge would fight to reconcile two
copies of the same ancestor commits for one commit's worth of real new content.
- **Course corrections found during execution** (squarely within "unify shared," not
separately asked): `libs/beheer` (admin/stamdata — already identical in both apps, not
actor-specific) and `libs/shared/styles.scss` (the token bridge, also byte-identical) were
folded in alongside `libs/shared/ui`. `auth` was deliberately **not** unified — ADR-0002
models it as actor-specific (different `Principal` variants), so today's accidental
similarity is expected to diverge, not something to force together.
- **Two Storybook instances, not one** (`.storybook-ssp/`, `.storybook-behandelportal/**`) —
a structural necessity, not a simplification choice: both apps' `auth` (and other) context
aliases share the name `@auth/*` but resolve to different physical directories, so no
single tsconfig can compile both apps' stories in one pass.
- Each app's own `shell/nav.config.ts` supplies its primary nav + admin links to the shared
`SiteHeaderComponent` via two new injection tokens (`HEADER_NAV_ITEMS`,
`HEADER_ADMIN_LINKS`) rather than the component hardcoding one app's routes — the same
"shared component takes copy as an input, the domain caller supplies it" idiom CLAUDE.md
already used for `shared/ui` copy, extended to injection tokens for this DI-shaped case.
The dev-only "state" panel (`DebugStateComponent`) moved out of `libs/shared` into
`apps/ssp` entirely (it's coupled to `BigProfileStore`, an ssp-only store) and is now hosted
by the shared `ShellComponent` via a `DEBUG_PANEL` injection token — no provider, no panel.
## Files
Nearly the whole repo, mechanically (`git mv`), plus:
- `angular.json` — rewritten for 4 projects: `ssp`, `behandelportal` (real apps),
`shared`, `beheer` (library projects whose only real purpose is giving `ng test` a
`buildTarget` to satisfy — see `libs/*/src/test-entry.ts`'s comment).
- `tsconfig.json` (root, no more `paths` — see its comment) + a new `tsconfig.json` per
app/library declaring that project's own full alias map.
- `.dependency-cruiser.base.js` (rule factory) + `.dependency-cruiser.ssp.js` +
`.dependency-cruiser.behandelportal.js` (replacing the single `.dependency-cruiser.js`).
- `eslint.config.mjs`, `package.json` scripts, `docker-compose.yml`, `.github/workflows/ci.yml`,
`scripts/ci-local.sh`, `scripts/check-tokens.sh`, `scripts/gen-snippets.mjs`,
`scripts/serve-i18n.mjs`, `scripts/dep-graph.sh`, `plopfile.mjs`, `nswag.json` — all
re-pointed at the new paths / made per-app aware.
- `CLAUDE.md`, `ARCHITECTURE.md`, `dependencies.md`, ADR-0002 — updated for the new layout.
## Steps
1. Branch, then `git mv` the SSP's `src/app/*` (minus `shared/`) → `apps/ssp/src/app/`, plus
`main.ts`/`index.html`/`locale/`/`proxy.conf.json`/`tsconfig.app.json`/`tsconfig.spec.json`.
2. `git mv src/app/shared` → `libs/shared/src`; `git mv src/app/beheer` → `libs/beheer/src`;
fold in `environments/` and the Storybook `docs/*.mdx` (both found byte-identical between
the two repos — same treatment as `shared/ui`).
3. Copy (not `git mv` — a different repo) behandelportal's `auth`/`behandeling` +
`main.ts`/`index.html`/`locale/` into `apps/behandelportal/src/`.
4. Rewrite `angular.json` for the two real projects + two library test-only projects; fix
every relative import that broke (`environments/environment` → `@shared/environments/environment`;
MDX docs' `../app/shared/...` → `../src/...`).
5. Split `.dependency-cruiser.js` into a base factory + one config per app; broaden
`eslint.config.mjs`'s `files` glob.
6. Rewrite `package.json` scripts, `docker-compose.yml` (added a `web-behandelportal`
service), `.github/workflows/ci.yml` + `scripts/ci-local.sh` (both apps built/tested,
path filters widened), the four `scripts/*.mjs`/`*.sh` helpers, `plopfile.mjs`'s three
generators (value-object/form-machine paths now resolve `shared`/`beheer` to `libs/`, the
`context` generator's tsconfig/dep-cruiser/routes edits re-anchored on the ssp config).
7. Split `.storybook/` into `.storybook-ssp/` + `.storybook-behandelportal/` (forced by the
`@auth/*` alias collision); moved the CIBG token bridge (`styles.scss`) and the generated
`documentation.json` to avoid two more collisions.
8. Add `HEADER_NAV_ITEMS`/`HEADER_ADMIN_LINKS`/`DEBUG_PANEL` injection tokens to
`SiteHeaderComponent`/`ShellComponent`; give each app its own `shell/nav.config.ts` and
(ssp only) `shell/debug-state/`.
9. Update `CLAUDE.md`, `ARCHITECTURE.md`, `dependencies.md`, and amend ADR-0002.
## Acceptance criteria
- [x] `npm run ci` green (lint, dep:check ×2, format:check, check:tokens, test:coverage ×4
projects, `ng build --localize` ×2, npm audit, backend test, snippets drift,
api-client drift).
- [x] Both apps' dev servers run against the one shared backend
(`npm start` / `npm run start:behandelportal`, ports 4200/4201, backend on 5000).
- [x] Both apps' production + localized builds succeed.
- [x] Both Storybook instances build (`build-storybook[:behandelportal]`).
- [x] `npm run gen:api` generates the one client into `libs/shared` with zero drift.
- [x] `docker compose up` serves both apps + the shared backend.
## Verification
`npm run ci`; `npx ng build ssp --localize && npx ng build behandelportal --localize`;
`npx ng run ssp:build-storybook && npx ng run behandelportal:build-storybook`; manual:
`npm start` on :4200 and `npm run start:behandelportal` on :4201 against
`dotnet run --project backend/src/BigRegister.Api`, log in on both.
## Out of scope
- Deleting or archiving the old sibling repo (`/home/eho/repos/behandelportal`) — left
untouched per the user's explicit choice.
- `scripts/create-frontend.mjs` / the `new-ssp` skill (bootstrapping a _third_ portal as a
fresh standalone repo) — whether future portals should also join this monorepo is a
separate decision.
- Extending the `storybook-a11y` CI job to behandelportal's own Storybook instance — it
still only covers ssp's, unchanged from before this WP.
- Reconciling `auth` between the two apps — left duplicated, deliberately (see Decisions).
## Risks
The `libs/shared`/`libs/beheer` "library" Angular projects exist solely to give the
unit-test builder a `buildTarget` to resolve (`@angular/build:unit-test` always requires
one, even for a project that's only ever tested) — `test-entry.ts` + `tsconfig.app.json` in
each are a `ponytail:`-flagged workaround, not a real buildable/publishable library. If
either library ever needs to actually build (e.g. an ng-packagr distributable), replace this
with a real library target then.