Skip to content

fix(install): the parafering seam and termijn timers never armed on a fresh install - #1729

Merged
rubenvdlinde merged 2 commits into
developmentfrom
fix/fresh-install-acceptance-defects
Sep 3, 2026
Merged

fix(install): the parafering seam and termijn timers never armed on a fresh install#1729
rubenvdlinde merged 2 commits into
developmentfrom
fix/fresh-install-acceptance-defects

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

What was broken

The "One engine" acceptance proof found seven fresh-install defects, each reproduced twice on a clean rig. They share a shape: the install reported success while the thing it installed did nothing.

A (HIGH) The parafering seam never armed

The shipped bvw templates declared besluitvormingActivate and besluitvormingPublish on the step. The engine's only action source is TransitionSpecReader::extractActions() over a transition, so "Start parafering" returned 200 with dispatchedActions: [], nothing was raised and nothing was logged.

Decision: the templates move, not the engine. The workflow-definition model spec gives steps and transitions each their own automaticActions, but the engine implements only the transition half. Teaching it to read step-level actions would silently redefine "actions on step completion" as "actions on status entry" for every app that ships a template, and no caller asked for that. So besluitvormingActivate now sits on the transition into Parafering and besluitvormingPublish on the transition into Bekendmaking, in all three bundles. A template that still declares actions at an unread position now logs a warning at activation, so the no-op is loud instead of silent.

B (HIGH) Nothing provisioned the group the shipped flow assigns to

dossiq_register.json assigns task-behandelaar to the group behandelaars. The completion gate resolves group membership, and membership of a group that does not exist is false for everyone, so the signal was refused fail-closed and the journey suite red 7/9.

A new idempotent repair step creates the group at install and upgrade. Membership stays the administrator's: dossiq never adds users. The flow is deliberately not reassigned to admin, which would hide a provisioning gap behind an over-privileged default. A backend that refuses group creation is reported loudly rather than swallowed. Documented in docs/admin/groups.md.

C (HIGH) The termijn seed wrote as Anonymous and called it success

SeedDeadlineMonitoringData ran with no session, OpenRegister RBAC refused every row, and the per-row failures were logged but never counted, so the step reported 0 definities (0 overgeslagen) as success. Zero TermijnDefinities existed on any fresh install, so no termijn timer could arm.

It now runs under the same system identity its sibling steps already use, and refused rows are counted, so a seed that seeded nothing cannot produce success-shaped output.

Mediums and lows

  • D The bvw resultTypes wrote English keep/destroy into the register's Dutch archivalAction enum, failing 9 objects at enable on every install.
  • E mandateArrangement declared a union type ["number","null"] on to_amount, which makes OpenRegister drop the whole schema on import, silently. Nullability is now expressed by omitting the property.
  • F The demo parafeerroutes were never held. The decision app's step schema accepts only person|body|role and refuses the route on anything else, so every route with a group actor came back "not handled". actorType is now translated rather than copied, and the schema's own advice spelling maps to advisory instead of falling back to a generic endorsement.
  • G The bvw caseTypes named no initial status, so an API-created case was born statusless; the verwerkingsactiviteit seeder called setters for columns OpenRegister has since renamed, failing all 7 rows with "naam is not a valid attribute"; and the ZGW-mapping repair threw on an empty schema context, which aborts an install.

The conformance test

BvwTemplateConformanceTest drives the real TransitionSpecReader over the shipped JSON, so it asserts what the engine actually sees rather than what the file looks like. It was proven red on all four axes before the fix. Three more sweeps close the same class:

  • RegisterSchemaUnionTypeTest sweeps every shipped schema for union types.
  • ProvisionAssignedGroupsTest sweeps the shipped flows, so a new group assignment cannot ship without its provisioning.
  • ParaferingDelegationServiceTest now maps every shipped route and asserts it lands inside the decision app's frozen vocabulary.

Each sweep asserts it found something before asserting it found nothing wrong, so an empty query cannot read as clean.

The Verwerkingsactiviteit test stub was a setter sink that accepted any attribute, so it could not fail. It now mirrors the real entity's declared properties and throws the way a QBMapper entity does, which is what makes G's seeder defect visible to the suite.

Verification

lint, phpcs (0 errors), phpmd (per subdir, both configs, clean), psalm (no errors), phpstan (no errors), phpunit 2849 tests green (3 skips, all pre-existing and verified on the unmodified tree).

Known gate note. gate-53 effective-manifest-crossref fails, and it is not from this change. Touching a register JSON makes the gate's scope indeterminate, so pre-existing findings start blocking. The finding is headerActions[0] on the Dashboard page carrying includeFields, fieldOverrides, size and columns, which the gate's action schema forbids. That block is byte-identical on development and this branch does not touch src/manifest.json. Removing those properties would regress #1713, which restored the New case field narrowing on purpose, so the mismatch belongs in the hydra-gates schema rather than here.

🤖 Generated with Claude Code

… fresh install

The "One engine" acceptance proof found seven fresh-install defects, each
reproduced twice on a clean rig. They share a shape: the install reported
success while the thing it installed did nothing.

A. The shipped bvw templates declared besluitvormingActivate and
besluitvormingPublish on the STEP. The engine's only action source is
TransitionSpecReader::extractActions() over a TRANSITION, so "Start
parafering" returned 200 with dispatchedActions: [] and no parafering was
ever raised. The templates move, not the engine: the workflow-definition
model spec gives steps and transitions each their own automaticActions,
but the engine implements only the transition half, and widening it would
have made "actions on step completion" mean "actions on status entry" for
every app. besluitvormingActivate now sits on the transition INTO
Parafering, besluitvormingPublish on the transition INTO Bekendmaking, in
all three bundles. A template that still declares actions at an unread
position now logs a warning at activation, so the no-op is loud.

B. The shipped case flow assigns task-behandelaar to the group
behandelaars, which nothing provisioned. The completion signal was refused
fail-closed for every actor and the journey suite red 7/9. A new
idempotent repair step creates the group at install; membership stays the
administrator's. The flow is NOT reassigned to admin: that would hide a
provisioning gap behind an over-privileged default.

C. SeedDeadlineMonitoringData wrote as Anonymous, RBAC refused every row,
and the failures were swallowed into "0 definities (0 overgeslagen)". It
now runs under the system identity its sibling steps already use, and
refused rows are COUNTED so a seed that seeded nothing cannot report
success-shaped output.

D. The bvw resultTypes wrote English archivalAction values into the
register's Dutch enum, failing 9 objects at enable on every install.

E. mandateArrangement declared a union type on to_amount, which makes
OpenRegister drop the WHOLE schema on import, silently.

F. The demo parafeerroutes were never held. The decision app's step schema
accepts only person|body|role and refuses the route on anything else, so
every route with a group actor came back "not handled". actorType is now
translated rather than copied, and the schema's own `advice` spelling maps
to advisory instead of falling back to a generic endorsement.

G. The bvw caseTypes named no initial status, so an API-created case was
born statusless; the verwerkingsactiviteit seeder called setters for
columns OpenRegister has since renamed, failing all 7 rows with "naam is
not a valid attribute"; and the ZGW-mapping repair threw on an empty
schema context, which aborts an install.

Tests. BvwTemplateConformanceTest drives the REAL TransitionSpecReader over
the shipped JSON and asserts every declared action sits where the engine
reads it, plus the enum and initial-status invariants. Proven red on all
four axes before the fix. RegisterSchemaUnionTypeTest sweeps every shipped
schema for union types; ProvisionAssignedGroupsTest sweeps the shipped
flows so a new group assignment cannot ship without its provisioning; and
ParaferingDelegationServiceTest now maps every SHIPPED route and asserts it
lands inside the decision app's frozen vocabulary. The
Verwerkingsactiviteit stub was a setter sink that accepted any attribute,
so it could not fail; it now mirrors the real entity's declared properties
and throws the way a QBMapper entity does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ma refuses

Fixing mandateArrangement's union type left the mock register's copy of
to_amount saying "null means unlimited". The mock already typed the property
as a plain number, so that sentence described a value neither schema accepts.

It also grew the untranslated-schema-string count by one and reddened the
check:schema-l10n ratchet: the extractor dedupes by string VALUE across
files, so the old wording survived here and the corrected wording landed as
a genuinely new string. Syncing the two retires the old string instead of
adding a second, which is why the count returns to its baseline rather than
needing the baseline raised.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ da44120

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
check-vue3-compile
test-l10n
format
check-schema-l10n
check-l10n-js
composer ✅ 106/106
npm ✅ 540/540
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-09-03 09:53 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ ee50dd2

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
check-vue3-compile
test-l10n
format
check-schema-l10n
check-l10n-js
composer ✅ 106/106
npm ✅ 540/540
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-09-03 10:13 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 1582f4a into development Sep 3, 2026
49 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant