Compare commits

...
2 Commits
Author SHA1 Message Date
ehoandClaude Sonnet 5 e4ea75414b docs(WP-61): mark done, record the shared-backend deviation
CI / changes (push) Successful in 7s
CI / lint (push) Successful in 56s
CI / frontend (push) Successful in 1m32s
CI / backend (push) Successful in 10s
CI / e2e (push) Successful in 2m56s
CI / semgrep (push) Successful in 1m10s
CI / storybook-a11y (push) Successful in 6m38s
CI / api-client-drift (push) Successful in 1m56s
WP-61 (bootstrap the behandelportal app) is done — a separate sibling repo
at /home/eho/repos/behandelportal, not a commit in this one. Records the
one real kickoff deviation from the vanilla new-ssp recipe: create-ssp.mjs
unconditionally renames the backend project even with --skip-backend, which
conflicts with the WP's "no new backend service" decision. Resolved by
deleting backend/ from the new repo and vendoring its swagger.json into
api-contract/ instead, with gen:api regenerating only the client against
that vendored doc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 23:26:37 +02:00
ehoandClaude Sonnet 5 920ce138cb fix(scaffolding): rename create-ssp to create-frontend, fix 2 bugs it surfaced
Renamed scripts/create-ssp.mjs -> create-frontend.mjs (+ its WP-45 doc, npm
script, and every prose/command reference) since "ssp" reads as an acronym
where "create-frontend" says what it does.

Also fixes two real bugs found while running it for real during WP-61:
scripts/ci-local.sh was missing from RENAME_CONTENT_FILES (any --name'd
clone that keeps a backend would break `npm run ci`, still hardcoding
BigRegister.slnx), and plopfile.mjs's `gen:context` insertion into
.dependency-cruiser.js anchored on the `showcase: null,` line, which
create-ssp/create-frontend has already stripped by the time gen:context
runs in the same invocation — silently leaving a freshly scaffolded
context with no CONTEXT_ALLOWED fence entry at all. Re-anchored on the
`const CONTEXT_ALLOWED = {` line instead, which never moves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 23:26:28 +02:00
8 changed files with 48 additions and 23 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ business content. Keep the machinery, replace the domain.
Clone this repo, `npm ci`, then mechanise the mechanical parts (WP-45):
```bash
node scripts/create-ssp.mjs --name Kvk --context inschrijving
node scripts/create-frontend.mjs --name Kvk --context inschrijving
```
`--name` (PascalCase) replaces `BigRegister.*` everywhere; `--context` (lowercase Dutch
@@ -65,7 +65,7 @@ Work through that checklist, keeping the GREEN gate below passing at every step.
`@registratie/ui/dashboard.page` — too much else hardcodes `/dashboard` (login's post-auth
redirect, `authGuard`'s fallback, header nav/logo, breadcrumb trail, several stories/specs).
The script rewrites its `loadComponent` to point at the freshly scaffolded `--context` page
instead (a `TODO(create-ssp)` stopgap landing page, not a real overview).
instead (a `TODO(create-frontend)` stopgap landing page, not a real overview).
- `scripts/gen-snippets.mjs` (showcase-only) + its `package.json` script entry + its CI/
`ci-local.sh` "showcase snippets drift" steps: deleted alongside `showcase/` — they run
unconditionally, so leaving them breaks `npm run ci` immediately once `showcase/` is gone.
+2 -2
View File
@@ -95,7 +95,7 @@ for its existing violations, so every WP ends green.
| [WP-42](WP-42-privacy-security-showcase.md) | Privacy & security showcase page (mask + no-PII log) | 8 · platform/DX/showcase | done |
| [WP-43](WP-43-scaffold-generators.md) | Runnable generators: value-object / form-machine (plop; ui-component/bff = skills) | 8 · platform/DX/showcase | done |
| [WP-44](WP-44-context-generator.md) | Runnable generator: `gen:context` | 8 · platform/DX/showcase | done |
| [WP-45](WP-45-create-ssp-generator.md) | `create-ssp` bootstrap generator (mechanise new-ssp) | 8 · platform/DX/showcase | done |
| [WP-45](WP-45-create-frontend-generator.md) | `create-frontend` bootstrap generator (mechanise new-ssp) | 8 · platform/DX/showcase | done |
| [WP-46](WP-46-vitest-coverage.md) | Vitest coverage (report + report-only thresholds) | 8 · platform/DX/showcase | done |
| [WP-47](WP-47-feature-flags.md) | Runtime feature flags (catalog-in-code, admin toggle, FE+backend) | 8 · platform/DX/showcase | done |
| [WP-48](WP-48-stamdata-deletion-protection.md) | Stamdata deletion protection (CI referential gate + editor expire/warn) | 8 · platform/DX/showcase | done |
@@ -135,7 +135,7 @@ Phase 8 (platform/DX/showcase, WP-37..46): goal is BOTH a teaching showcase AND
starter template. Priority: WP-37 (P0 quick fix) → WP-46 + WP-38 + WP-39 (P1, parallel) →
WP-40 (P2) → WP-43 (P3) → WP-41 → WP-42 → WP-44 → WP-45 (P4). Ordering constraints:
41 needs 40's PII kernel; 42 needs 40+41; 44 (`gen:context`) needs 38 (declarative boundaries
make the generator simple) + 43; 45 (`create-ssp`) needs 43+44. 37/38/39/40/43/46 are otherwise
make the generator simple) + 43; 45 (`create-frontend`) needs 43+44. 37/38/39/40/43/46 are otherwise
independent. Two open tool forks, decided as step 1 of their WP: 38 dependency-cruiser vs Sheriff;
43 plop vs Angular schematics.
Phase 9 (OpenZaak/ZGW integration, WP-49..52) is strictly ordered 49 → 50 → 51 → 52: 49 lands
@@ -1,4 +1,4 @@
# WP-45 — `create-ssp` bootstrap generator
# WP-45 — `create-frontend` bootstrap generator
Status: done (ad7ca31)
Phase: 8 — platform/DX/showcase
@@ -13,7 +13,7 @@ scaffold keeping the enforced architecture + shared building blocks.
## Decisions
- A `create-ssp` script (Node/plop-driven) that clones-and-strips per the `new-ssp` recipe:
- A `create-frontend` script (Node/plop-driven) that clones-and-strips per the `new-ssp` recipe:
drop the business contexts, prune their routes/aliases/boundary entries, rename `BigRegister.*`,
re-point the `--rhc-*` token bridge, re-seed the backend, re-run `gen:api`.
- Keep-as-is list from `new-ssp` (shared kernel/ui/layout, eslint/boundary config, check-tokens,
@@ -22,7 +22,7 @@ scaffold keeping the enforced architecture + shared building blocks.
## Files
- New `scripts/create-ssp.*` (+ any templates); documented in `.claude/skills/new-ssp/SKILL.md`.
- New `scripts/create-frontend.*` (+ any templates); documented in `.claude/skills/new-ssp/SKILL.md`.
## Acceptance criteria
@@ -1,6 +1,6 @@
# WP-61 — Bootstrap the behandelportal app
Status: todo
Status: done (behandelportal@6b0c6ce — separate sibling repo, not a commit in this one)
Phase: 11 — Behandelportal
## Why
@@ -23,8 +23,29 @@ with no business context yet — an empty, correctly-scaffolded shell.
tooling/CI gates, ADRs 0001-0003; strip the four citizen contexts and citizen branding.
- The new app talks to the same `BigRegister.Api` backend — no new backend service
(confirmed by ADR-0002: contexts integrate through the backend).
- Repo layout for the new app (separate repo vs. a second app in this monorepo) — decide
at kickoff based on how `new-ssp` is meant to be invoked.
- Repo layout: **separate sibling repo** at `/home/eho/repos/behandelportal` (fresh clone —
`scripts/create-frontend.mjs`'s own header comment says to run it inside a fresh clone, not
against this repo's working tree).
## Resolution (kickoff deviation from the vanilla recipe)
`create-frontend.mjs` unconditionally renames the backend project regardless of
`--skip-backend` (that flag only gates the `gen:api` regen step) — which conflicts with
"no new backend service" above. Resolved by running the script as-is
(`--name Behandelportal --context behandeling --skip-backend`), then in the new repo:
deleting `backend/` entirely, vendoring its `swagger.json` into `api-contract/swagger.json`,
and pointing `gen:api` at `nswag run nswag.json` against that vendored doc instead of
regenerating from a live backend. Dev workflow is two terminals: `BigRegister.Api` runs from
_this_ repo on `:5000`, Behandelportal serves on `:4201` via its own unchanged
`proxy.conf.json`.
Also found and fixed by hand (real bugs in `create-frontend.mjs`, not specific to this WP):
`plop context`'s `.dependency-cruiser.js` insertion anchors on the `showcase`
`CONTEXT_ALLOWED` entry, which is already stripped by the time `plop context` runs in the
same invocation — so a freshly scaffolded context silently gets **no boundary-fence
entry**, and `dep:check` passes green with the architecture guarantee simply absent. Not
re-fixed in `create-frontend.mjs` itself this pass (out of scope here) — just compensated for by
hand in the new repo.
## Files
@@ -42,10 +63,10 @@ workspace reference, CI config) — enumerate at kickoff by following the skill.
## Acceptance criteria
- [ ] New app boots and its own `npm run ci` is green.
- [ ] No citizen-facing business context (`registratie`, `herregistratie`, `brief`,
- [x] New app boots and its own `npm run ci` is green.
- [x] No citizen-facing business context (`registratie`, `herregistratie`, `brief`,
`showcase`) present.
- [ ] Points at the same backend (`BigRegister.Api`) as this repo, no new backend stood
- [x] Points at the same backend (`BigRegister.Api`) as this repo, no new backend stood
up.
## Verification
+1 -1
View File
@@ -71,7 +71,7 @@ step-by-step and its GREEN-at-every-step verify gate.
- **`gen:context`** ([WP-44](../project/backlog/WP-44-context-generator.md)) — a runnable
version of the `new-context` skill. Planned, not shipped.
- **`create-ssp`** ([WP-45](../project/backlog/WP-45-create-ssp-generator.md)) —
- **`create-frontend`** ([WP-45](../project/backlog/WP-45-create-frontend-generator.md)) —
mechanised `new-ssp`. Planned, not shipped.
Don't assume either command exists — use the corresponding skill until they land.
+1 -1
View File
@@ -24,7 +24,7 @@
"gen:value-object": "plop value-object",
"gen:form-machine": "plop form-machine",
"gen:context": "plop context",
"create-ssp": "node scripts/create-ssp.mjs",
"create-frontend": "node scripts/create-frontend.mjs",
"serve:i18n": "ng build --configuration development --localize && node scripts/serve-i18n.mjs",
"ci": "bash scripts/ci-local.sh",
"e2e": "playwright test",
+7 -4
View File
@@ -107,12 +107,15 @@ export default function (plop) {
template: '"paths": {\n "@{{kebabCase name}}/*": ["src/app/{{kebabCase name}}/*"],\n',
},
{
// Boundary entry (WP-38's single source of truth) — inserted right before the
// `showcase: null` line, which never moves.
// Boundary entry (WP-38's single source of truth) — inserted right after the
// `const CONTEXT_ALLOWED = {` opening line, which never moves. (Previously anchored
// on the `showcase: null,` line, but create-frontend.mjs strips that entry before running
// this generator, so the modify silently no-op'd and the new context got no fence at
// all — dep:check stayed green with the boundary rule simply missing.)
type: 'modify',
path: '.dependency-cruiser.js',
pattern: /(\s*)showcase: null,/,
template: "$1'{{kebabCase name}}': [],$1showcase: null,",
pattern: /const CONTEXT_ALLOWED = \{\n/,
template: 'const CONTEXT_ALLOWED = {\n {{kebabCase name}}: [],\n',
},
{
// Lazy route — inserted right before the catch-all, which never moves.
@@ -9,8 +9,8 @@
// from nothing — those steps print an explicit checklist instead of pretending to solve them.
//
// Usage:
// node scripts/create-ssp.mjs --name Kvk --context inschrijving
// node scripts/create-ssp.mjs --name Kvk --context inschrijving --keep registratie --dry-run
// node scripts/create-frontend.mjs --name Kvk --context inschrijving
// node scripts/create-frontend.mjs --name Kvk --context inschrijving --keep registratie --dry-run
//
// --name <PascalName> replaces BigRegister.* everywhere (required)
// --context <name> lowercase Dutch ubiquitous term, passed to `plop context` (required)
@@ -43,7 +43,7 @@ function parseArgs(argv) {
function usageAndExit(message) {
if (message) console.error(message + '\n');
console.error(
'Usage: node scripts/create-ssp.mjs --name <PascalName> --context <lowercase-term> ' +
'Usage: node scripts/create-frontend.mjs --name <PascalName> --context <lowercase-term> ' +
'[--keep <context>] [--dry-run] [--skip-backend]',
);
process.exit(1);
@@ -351,6 +351,7 @@ const RENAME_CONTENT_FILES = [
'playwright.config.ts',
'README.md',
'backend/README.md',
'scripts/ci-local.sh',
];
const SKIP_DIRS = new Set(['bin', 'obj', 'node_modules', '.git']);
@@ -465,7 +466,7 @@ function repointDashboard(contextName, registratieWasStripped, args) {
const pageClass = `${pascalCase(contextName)}Page`;
const next = content.replace(
/loadComponent: \(\) => import\('@registratie\/ui\/dashboard\.page'\)\.then\(\(m\) => m\.DashboardPage\),/,
`// TODO(create-ssp): stopgap landing page — point this at a real overview once you have one.\n` +
`// TODO(create-frontend): stopgap landing page — point this at a real overview once you have one.\n` +
` loadComponent: () =>\n` +
` import('@${contextName}/ui/${contextName}.page').then((m) => m.${pageClass}),`,
);