Commit Graph
3 Commits
Author SHA1 Message Date
ehoandClaude Opus 5 7a29f5facc feat(brief): tolerate a 404 on GET /brief with a one-shot reset (RB-22)
BriefStore.load() now treats a 404 from GET /brief as "no brief exists
yet" and calls the existing reset() command once, instead of showing
the generic load-failed error. BriefAdapter.load() gains a
BriefLoadFailure error channel (notFound | error) so the store can
tell a 404 apart from every other failure; every other adapter method
stays on runSubmit, unchanged.

The once-only bound is a field on the store, not a comment: a second
404 (from a later load() call) always falls through to the ordinary
error path, and the recovery path never calls load() again, so no
loop can form.

This is the expand half of CQ-007's split (04-cqrs-light.md). Today's
backend never 404s GET /brief, so the new branch is dead code until
RB-23 (the backend contract half) ships in a later merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 18:43:27 +02:00
ehoandClaude Opus 5 772c47ea43 fix(brief): keep the dev hatches out of production builds (RB-11)
BIO-012: roleInterceptor/subjectInterceptor are correctly registered
only under isDevMode(), but three hand-written fetch adapters
(reveal-bignummer, letter-preview, org-template's proefbrief) bypass
HttpClient and set X-Role/X-Subject themselves with no guard. The
readers underneath, role.ts and subject.ts, were ungated too: they
read ?role=/?subject= and wrote it into sessionStorage on any
navigation, in any build -- for ?subject= that value is a BSN, which
is exactly what SessionStore's G1 comment promises never happens.

Gate both layers: currentRole()/currentSubject() return their safe
default immediately outside isDevMode() (no query-param read, no
sessionStorage write), and the three adapters additionally wrap their
headers in isDevMode() so a production request carries neither header
at all, matching what an HttpClient request already does once the
interceptors aren't registered.

TE-002: reveal-bignummer's response-shape validation was a "Trust
boundary" a spec could only reach by stubbing globalThis.fetch.
Exported it as parseRevealed(body), matching the other 30 parse*
boundaries in the repo. Same treatment for letter-preview's
errorMessage and org-template's proefbrief error mapping (extracted
from an inline try/catch into a named, exported function first, since
it wasn't already separate).

BIO-006(a): reveal-bignummer sent X-Step-Up: 'true' unconditionally,
so the backend's step-up precondition constrained nothing. reveal()
now takes a stepUp flag; BriefStore.revealBigNummer() -- reachable
only after the UI's confirm() gesture -- is the one that supplies it,
so the literal no longer lives in the transport adapter.

BIO-006(b): documented in roles-and-access.md that drafter is also
the backend's fallback identity (StubIdentityProvider's catch-all
arm), not just the dev switcher's initial choice -- so the
least-privilege consequence of it also being the only role that may
reveal a BSN is visible.

Doc correction, same diff: roles-and-access.md's "wired only under
isDevMode()" claim was false for the three hand-written fetch paths;
it now says where the gate lives (interceptor registration and the
reader functions) so it doesn't go stale the same way again.
CLAUDE.md's dev-only claims needed no correction -- they already
noted these three calls bypass the interceptor.

Every fix has a test confirmed red by temporarily reverting the
source change and rerunning the suite before restoring it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 14:20:47 +02:00
ehoandClaude Sonnet 5 e7156c5132 feat(WP-67): merge behandelportal into this repo as a monorepo
Restructures into apps/ssp + apps/behandelportal (two Angular projects)
plus libs/shared + libs/beheer (cross-app libraries), replacing WP-61's
separate sibling repo. That split had already produced real drift: a
hand-vendored copy of the backend's OpenAPI doc, a shared/ui+layout tree
forked and silently diverging (7 files), and beheer + the styles.scss
token bridge duplicated byte-for-byte across both repos.

- git mv the SSP's src/app/* into apps/ssp/; fold shared/, beheer/,
  environments/, the Storybook docs/*.mdx, and styles.scss into
  libs/shared + libs/beheer (all confirmed identical between the two
  repos before merging). auth stays deliberately duplicated per
  ADR-0002 (actor-specific, expected to diverge) - amended there.
- One generated API client (libs/shared), no more vendored swagger.json.
- .dependency-cruiser split into a base factory + one config per app,
  and Storybook into .storybook-ssp/.storybook-behandelportal - both
  forced by the @auth/* alias resolving to different directories per app.
- SiteHeaderComponent/ShellComponent gained HEADER_NAV_ITEMS/
  HEADER_ADMIN_LINKS/DEBUG_PANEL injection tokens so each app supplies
  its own nav/admin-links/dev-panel instead of one being hardcoded.
- CLAUDE.md, ARCHITECTURE.md, dependencies.md, and ADR-0002 updated;
  WP-67 backlog entry documents the full decision trail.

npm run ci green (lint, dep:check x2, 360 tests across ssp/
behandelportal/shared/beheer, both localized builds, backend tests,
snippet + api-client drift); both dev servers, both Storybook
instances, and docker compose verified working.

The old sibling repo (/home/eho/repos/behandelportal) is left
untouched, not deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 21:01:57 +02:00