docs(test): generated living behaviour spec + FE/BE seam drift check (WP-71)

Gherkin/Cucumber was considered and rejected for business-readable BDD
scenarios: step-binding by runtime string match undoes the compile-time
guarantees WP-70 just added, and needs two frameworks for .NET+TS with
no non-technical co-author in view. Instead scripts/gen-behaviour-spec.mjs
(modeled on the existing gen-snippets.mjs) extracts every describe/it
and [Fact]/[Theory] name straight from the real suites into
libs/shared/docs/behaviour-spec.mdx, gated for drift in CI exactly like
gen-snippets/gen-api — the page can never diverge from the tests because
it's generated from them, and test names stay the single source of truth.

scripts/check-seam.sh guards the one FE/BE rule duplication most likely
to silently diverge: IntakePolicy.cs's ScholingThreshold vs
intake.machine.ts's SCHOLING_THRESHOLD_DEFAULT, two unlinked literals
pinned separately in each side's own tests but never against each other.

package.json/CI wiring for both (gen:behaviour-spec, check:seam) shipped
in the prior commit alongside the typecheck gate, since all three touch
the same few config files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-08-18 20:25:17 +02:00
co-authored by Claude Sonnet 5
parent 28c0a250e7
commit 306d002221
4 changed files with 1345 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# WP-71 (Track E): fail if the backend's scholing-threshold policy default and the frontend's
# offline fallback default drift apart. ADR-0001's "config value" shape means the backend is
# the authority (GET /intake/policy) and the FE only keeps SCHOLING_THRESHOLD_DEFAULT as an
# offline/first-paint fallback (intake.machine.ts) — but the two literals are otherwise
# unlinked, so nothing stops them silently diverging. This is a cheap grep-based tripwire, not
# a build-time link between the two languages.
set -uo pipefail
BACKEND_FILE='backend/src/BigRegister.Api/Domain/Intake/IntakePolicy.cs'
FRONTEND_FILE='apps/ssp/src/app/herregistratie/domain/intake.machine.ts'
backend_value=$(grep -oE 'ScholingThreshold\s*=\s*[0-9]+' "$BACKEND_FILE" | grep -oE '[0-9]+$')
frontend_value=$(grep -oE 'SCHOLING_THRESHOLD_DEFAULT\s*=\s*[0-9]+' "$FRONTEND_FILE" | grep -oE '[0-9]+$')
if [ -z "$backend_value" ]; then
echo "FAIL: could not find IntakePolicy.ScholingThreshold in $BACKEND_FILE"
exit 1
fi
if [ -z "$frontend_value" ]; then
echo "FAIL: could not find SCHOLING_THRESHOLD_DEFAULT in $FRONTEND_FILE"
exit 1
fi
if [ "$backend_value" != "$frontend_value" ]; then
echo "FAIL: FE/BE seam drift on the scholing threshold default"
echo " $BACKEND_FILE: ScholingThreshold = $backend_value"
echo " $FRONTEND_FILE: SCHOLING_THRESHOLD_DEFAULT = $frontend_value"
echo 'Both literals represent the same intake policy default (ADR-0001 config value) and must match.'
exit 1
fi
echo "OK: scholing threshold default matches on both sides ($backend_value)"