The three specs shared one mutable backend and said so in their own
comments ("Restart the backend between CI runs"). WP-70 recorded the fix as
a dev-only seed endpoint; it isn't needed. The DB path already routes
through IConfiguration, so playwright.config's webServer hands the backend a
throwaway SQLite file per invocation — the same trick TestWebApplicationFactory
already uses, with zero backend change. And StubIdentityProvider already
honoured X-Subject; the only gap was that nothing sent it. That matters
because the backend has no IsDevelopment() gate anywhere, so a seed endpoint
would have had to invent the codebase's first environment gate.
subjectInterceptor mirrors the existing roleInterceptor and is wired into the
same isDevMode()-only list. Interceptors alone were not enough: the raw XHR
upload and the hand-written letter-preview fetch bypass Angular's chain (as
CLAUDE.md documents), so both now stamp X-Subject explicitly — without that,
every uploaded document still landed under DemoOwner.
reuseExistingServer stays on: flipping it would break local runs for anyone
already serving the docker stack. Each run gets a unique DB filename and
global-setup sweeps only prior runs' leftovers — deleting a fixed path
mid-run risks SQLite silently recreating an empty, unmigrated file under
fullyParallel.
Verified: e2e passes twice back-to-back with no backend restart, and
X-Subject was observed on a real request, not merely wired.
brief-v2.spec.ts keeps the shared identity for now — see the KNOWN GAP note;
a backend staleness bug makes /brief/preview return a sent letter with the
draft watermark for any non-DemoOwner BSN. actors.ts reserves the actor for
whoever fixes it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The demo BSN, password, and DigiD login sequence were copy-pasted verbatim
into all three specs; the diploma id #diploma-d1 was coupled to SeedData.cs's
ordering by comment only, with no compile-time check if the seed shape
changed. e2e/support/actors.ts names both: Actors.zorgverlener + loginAs()
for the login sequence, SeedRefs.diplomaZonderPolicyVragen for the seed
coupling (with the "why d1" reasoning attached to the name, not scattered
across specs). Zero assertions changed — pure extract-and-rename of test
setup. e2e test-isolation (the shared mutable backend) is a documented
follow-up, not fixed here — see ADR-0006.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Local semgrep run found 25 findings (not the WP's remembered 27 — already-stale
by the time this ran): dependabot cooldown, npm min-release-age, every GitHub
Action pinned to a full commit SHA (dependabot's existing github-actions
ecosystem entry keeps these current), and 2 detect-non-literal-regexp findings
in e2e/create-ssp.mjs suppressed as false positives (non-attacker-controlled
input: a test's own captured version number, a local generator's CLI arg).
`semgrep scan` now runs with `--error`, a real blocking gate instead of
report-only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- backend: dotnet format the WP-51 migration (2-space indent, no BOM)
to match .editorconfig — dotnet format --verify-no-changes was failing.
- storybook: stub FeatureFlagStore (WP-47) in shell/site-header stories
alongside AccessStore, fixing NG0201 no-provider errors; bump the
storybook-a11y container's memory cap 4g→6g (build-storybook +
compodoc measured ~5.8GB peak RSS, leaving too little headroom).
- backend: fix a startup-breaking bug in the new (WP-52) POST
/zgw/notificaties handler — it took ZgwOptions as a minimal-API
parameter, which isn't registered in DI, so ASP.NET's endpoint-table
build threw on every request once the route was registered (incl.
/swagger, which is why Playwright's webServer health check timed
out). Close over the existing `zgw` local instead.
- e2e: brief-v2.spec.ts's "Voorbeeld" button locator was ambiguous
once a second "Voorbeeld met testwaarden" button existed (Playwright
name matching is substring-based) — added `exact: true`. Also fixed
the sent-letter preview flow to match app-letter-composer's actual
behavior (single click → fetch, no in-page dialog, unlike
app-behandel-scherm's), and fixed a watermark assertion that checked
for the always-present `.preview-watermark` CSS class name instead
of the conditionally-rendered "VOORBEELD" marker text.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Closes phase 6 (Brief v2): a demo script mapping shipped scenarios to
URL+click paths (no Brief v2 PRD ever existed to translate one from —
written directly against the code instead), one e2e spec covering
compose→approve→send and admin republish→drafter-sees-it, and
Storybook state gaps (rejection diff, read-only viewer, org logo,
upload rejection) that prior WPs left uncovered. Flags passage-picker
as dead code, superseded by besluit-panel.
npm run e2e is not verified green in this sandbox — see WP-28's
Deviations section; the pre-existing, unmodified smoke.spec.ts fails
identically here, pointing at a sandbox rendering issue rather than a
regression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Branded Bsn value object with the elfproef (11-test) checksum in shared/kernel/bsn.ts,
wired into the DigiD login boundary so login does real BSN validation (hint + e2e BSNs
updated to a valid 123456782). Consolidate the pure maskers into shared/kernel/pii.ts
(maskBsn/maskTail/REDACTED); debug-state keeps redactProfile (needs the registratie
BigProfile — boundary). New <app-masked-value> atom (+story) centralises the masked
`.includes('*')` detection + reveal affordance; behandel-scherm refactored onto it.
Session.bsn stays string (persistence boundary drops it for privacy). +specs for bsn/pii.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a happy-path spec (login → dashboard → registratie wizard, including
a real identity-document upload → real submit) and a degraded-path spec
(?scenario=error → <app-async> error slot → retry), both driving the real
app against the real .NET backend, plus a CI job that boots both.
Writing the retry spec surfaced a real bug: AsyncComponent's retry() only
reloads a [resource]-fed instance, so every real page (all [data]-fed via
a store's RemoteData) had a silently no-op retry button. Added a
retryClicked output and wired it on the dashboard's two async blocks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>